Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a68c9e3d authored by qctecmdr Service's avatar qctecmdr Service Committed by Gerrit - the friendly Code Review server
Browse files

Merge "radio: RTC6226: add support to power on/off rtc6226 chip"

parents 6664f68a 8db5cfb6
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -508,7 +508,7 @@ endif # V4L_RADIO_ISA_DRIVERS

config I2C_RTC6226_QCA
	tristate "Richwave RTC6226 FM Radio Receiver support with I2C for QCA"
	depends on I2C && RADIO_RTC6226_QCA
	depends on I2C && VIDEO_V4L2
	---help---
	  This is a driver for I2C devices with the Richwave RTC6226
	  chip.
+2 −0
Original line number Diff line number Diff line
@@ -1686,6 +1686,8 @@ int rtc6226_vidioc_g_ctrl(struct file *file, void *priv,
	case V4L2_CID_PRIVATE_RTC6226_RDSGROUP_PROC:
		break;
	case V4L2_CID_PRIVATE_RTC6226_SIGNAL_TH:
	/* intentional fallthrough */
	case V4L2_CID_PRIVATE_RTC6226_RSSI_TH:
		ctrl->value = radio->rssi_th;
		break;
	default:
+256 −25
Original line number Diff line number Diff line
@@ -390,6 +390,133 @@ int rtc6226_enable_irq(struct rtc6226_device *radio)
	return retval;
}

static int rtc6226_fm_vio_reg_cfg(struct rtc6226_device *radio, bool on)
{
	int rc = 0;
	struct fm_power_vreg_data *vreg;

	vreg = radio->vioreg;
	if (!vreg) {
		pr_err("In %s, vio reg is NULL\n", __func__);
		return rc;
	}
	if (on) {
		pr_debug("vreg is : %s\n", vreg->name);
		rc = regulator_set_voltage(vreg->reg,
					vreg->low_vol_level,
					vreg->high_vol_level);
		if (rc < 0) {
			pr_err("set_vol(%s) fail %d\n", vreg->name, rc);
			return rc;
		}
		rc = regulator_enable(vreg->reg);
		if (rc < 0) {
			pr_err("reg enable(%s) failed.rc=%d\n", vreg->name, rc);
				regulator_set_voltage(vreg->reg,
						0,
						vreg->high_vol_level);
			return rc;
		}
		vreg->is_enabled = true;

	} else {
		rc = regulator_disable(vreg->reg);
		if (rc < 0) {
			pr_err("reg disable(%s) fail rc=%d\n", vreg->name, rc);
			return rc;
		}
		vreg->is_enabled = false;

		/* Set the min voltage to 0 */
		rc = regulator_set_voltage(vreg->reg,
					0,
					vreg->high_vol_level);
		if (rc < 0) {
			pr_err("set_vol(%s) fail %d\n", vreg->name, rc);
			return rc;
		}
	}
	return rc;
}

static int rtc6226_fm_vdd_reg_cfg(struct rtc6226_device *radio, bool on)
{
	int rc = 0;
	struct fm_power_vreg_data *vreg;

	vreg = radio->vddreg;
	if (!vreg) {
		pr_err("In %s, vdd reg is NULL\n", __func__);
		return rc;
	}

	if (on) {
		pr_debug("vreg is : %s\n", vreg->name);
		rc = regulator_set_voltage(vreg->reg,
					vreg->low_vol_level,
					vreg->high_vol_level);
		if (rc < 0) {
			pr_err("set_vol(%s) fail %d\n", vreg->name, rc);
			return rc;
		}

		rc = regulator_enable(vreg->reg);
		if (rc < 0) {
			pr_err("reg enable(%s) failed.rc=%d\n", vreg->name, rc);
			regulator_set_voltage(vreg->reg,
					0,
					vreg->high_vol_level);
			return rc;
		}
		vreg->is_enabled = true;
	} else {
		rc = regulator_disable(vreg->reg);
		if (rc < 0) {
			pr_err("reg disable(%s) fail. rc=%d\n", vreg->name, rc);
			return rc;
		}
		vreg->is_enabled = false;

			/* Set the min voltage to 0 */
		rc = regulator_set_voltage(vreg->reg,
					0,
					vreg->high_vol_level);
		if (rc < 0) {
			pr_err("set_vol(%s) fail %d\n", vreg->name, rc);
			return rc;
		}
	}
	return rc;
}

static int rtc6226_fm_power_cfg(struct rtc6226_device *radio, bool powerflag)
{
	int rc = 0;

	if (powerflag) {
		/* Turn ON sequence */
		rc = rtc6226_fm_vdd_reg_cfg(radio, powerflag);
		if (rc < 0) {
			pr_err("In %s, vdd reg cfg failed %x\n", __func__, rc);
			return rc;
		}
		rc = rtc6226_fm_vio_reg_cfg(radio, powerflag);
		if (rc < 0) {
			pr_err("In %s, vio reg cfg failed %x\n", __func__, rc);
			rtc6226_fm_vdd_reg_cfg(radio, false);
			return rc;
		}
	} else {
		/* Turn OFF sequence */
		rc = rtc6226_fm_vdd_reg_cfg(radio, powerflag);
		if (rc < 0)
			pr_err("In %s, vdd reg cfg failed %x\n", __func__, rc);
		rc = rtc6226_fm_vio_reg_cfg(radio, powerflag);
		if (rc < 0)
			pr_err("In %s, vio reg cfg failed %x\n", __func__, rc);
	}
	return rc;
}
/*
 * rtc6226_fops_open - file open
 */
@@ -415,13 +542,30 @@ int rtc6226_fops_open(struct file *file)
	INIT_DELAYED_WORK(&radio->work_scan, rtc6226_scan);
	INIT_WORK(&radio->rds_worker, rtc6226_rds_handler);

	/* Power up  Supply voltage to VDD and VIO */
	retval = rtc6226_fm_power_cfg(radio, TURNING_ON);
	if (retval) {
		pr_err("%s: failed to supply voltage\n", __func__);
		goto open_err_setup;
	}

	retval = rtc6226_enable_irq(radio);
	/* Wait for the value to take effect on gpio. */
	msleep(100);
	rtc6226_enable_irq(radio);
	if (retval) {
		pr_err("%s:enable irq failed\n", __func__);
		goto open_err_req_irq;
	}

	if (retval)
		v4l2_fh_release(file);
	return retval;

open_err_req_irq:
	rtc6226_fm_power_cfg(radio, TURNING_OFF);
open_err_setup:
	radio->users--;
	return retval;
}

/*
@@ -430,6 +574,7 @@ int rtc6226_fops_open(struct file *file)
int rtc6226_fops_release(struct file *file)
{
	struct rtc6226_device *radio = video_drvdata(file);
	int retval = 0;

	pr_info("%s : Exit\n", __func__);
	if (v4l2_fh_is_singular_file(file)) {
@@ -440,6 +585,9 @@ int rtc6226_fops_release(struct file *file)
	}
	rtc6226_disable_irq(radio);
	radio->users--;
	retval = rtc6226_fm_power_cfg(radio, TURNING_OFF);
	if (retval < 0)
		pr_err("%s: failed to apply voltage\n", __func__);
	return v4l2_fh_release(file);
}

@@ -520,6 +668,23 @@ static int rtc6226_pinctrl_init(struct rtc6226_device *radio)
	return retval;
}

static int rtc6226_dt_parse_vreg_info(struct device *dev,
			struct fm_power_vreg_data *vreg, const char *vreg_name)
{
	int ret = 0;
	u32 vol_suply[2];
	struct device_node *np = dev->of_node;

	ret = of_property_read_u32_array(np, vreg_name, vol_suply, 2);
	if (ret < 0) {
		pr_err("Invalid property name\n");
		ret =  -EINVAL;
	} else {
		vreg->low_vol_level = vol_suply[0];
		vreg->high_vol_level = vol_suply[1];
	}
	return ret;
}

/*
 * rtc6226_i2c_probe - probe for the device
@@ -530,6 +695,8 @@ static int rtc6226_i2c_probe(struct i2c_client *client,
	struct rtc6226_device *radio;
	struct v4l2_device *v4l2_dev;
	struct v4l2_ctrl_handler *hdl;
	struct regulator *vddvreg = NULL;
	struct regulator *viovreg = NULL;
	int retval = 0;
	int i = 0;
	int kfifo_alloc_rc = 0;
@@ -540,21 +707,45 @@ static int rtc6226_i2c_probe(struct i2c_client *client,
	pr_info("%s enter\n", __func__);
	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
		retval = -ENODEV;
		goto err_initial;
		return retval;
	}

	/*
	 * if voltage regulator is not ready yet, return the error
	 * if error is -EPROBE_DEFER to kernel then probe will be called at
	 * later point of time.
	 */
	viovreg = regulator_get(&client->dev, "vio");
	if (IS_ERR(viovreg)) {
		retval = PTR_ERR(viovreg);
		pr_err("%s: regulator_get(vio) failed. retval=%d\n",
			__func__, retval);
		return retval;
	}

	vddvreg = regulator_get(&client->dev, "vdd");
	if (IS_ERR(vddvreg)) {
		retval = PTR_ERR(vddvreg);
		pr_err("%s: regulator_get(vdd) failed. retval=%d\n",
			__func__, retval);
		regulator_put(viovreg);
		return retval;
	}

	/* private data allocation and initialization */
	radio = kzalloc(sizeof(struct rtc6226_device), GFP_KERNEL);
	if (!radio) {
		retval = -ENOMEM;
		goto err_initial;
		regulator_put(viovreg);
		regulator_put(vddvreg);
		return retval;
	}

	v4l2_dev = &radio->v4l2_dev;
	retval = v4l2_device_register(&client->dev, v4l2_dev);
	if (retval < 0) {
		pr_err("%s couldn't register v4l2_device\n", __func__);
		goto err_radio;
		goto err_vreg;
	}

	pr_info("v4l2_device_register successfully\n");
@@ -568,10 +759,47 @@ static int rtc6226_i2c_probe(struct i2c_client *client,
	retval = rtc6226_parse_dt(&client->dev, radio);
	if (retval) {
		pr_err("%s: Parsing DT failed(%d)\n", __func__, retval);
		kfree(radio);
		return retval;
		goto err_v4l2;
	}

	radio->vddreg = devm_kzalloc(&client->dev,
				sizeof(struct fm_power_vreg_data),
				GFP_KERNEL);
	if (!radio->vddreg) {
		pr_err("%s: allocating memory for vdd vreg failed\n",
							__func__);
		retval = -ENOMEM;
		goto err_v4l2;
	}

	radio->vddreg->reg = vddvreg;
	radio->vddreg->name = "vdd";
	radio->vddreg->is_enabled = false;
	retval = rtc6226_dt_parse_vreg_info(&client->dev,
			radio->vddreg, "rtc6226,vdd-supply-voltage");
	if (retval < 0) {
		pr_err("%s: parsing vdd-supply failed\n", __func__);
		goto err_v4l2;
	}

	radio->vioreg = devm_kzalloc(&client->dev,
				sizeof(struct fm_power_vreg_data),
				GFP_KERNEL);
	if (!radio->vioreg) {
		pr_err("%s: allocating memory for vio vreg failed\n",
							__func__);
		retval = -ENOMEM;
		goto err_v4l2;
	}
	radio->vioreg->reg = viovreg;
	radio->vioreg->name = "vio";
	radio->vioreg->is_enabled = false;
	retval = rtc6226_dt_parse_vreg_info(&client->dev,
			radio->vioreg, "rtc6226,vio-supply-voltage");
	if (retval < 0) {
		pr_err("%s: parsing vio-supply failed\n", __func__);
		goto err_v4l2;
	}
	/* Initialize pin control*/
	retval = rtc6226_pinctrl_init(radio);
	if (retval) {
@@ -584,15 +812,6 @@ static int rtc6226_i2c_probe(struct i2c_client *client,
		pr_info("%s rtc6226_pinctrl_init success\n", __func__);
	}

	if (radio->ext_ldo_gpio > 0) {
		retval = gpio_direction_output(radio->ext_ldo_gpio, 1);
		if (retval) {
			pr_err("%s Unable to set direction(LDO)\n", __func__);
			kfree(radio);
			return retval;
		}
	}

	memcpy(&radio->videodev, &rtc6226_viddev_template,
		sizeof(struct video_device));

@@ -605,7 +824,7 @@ static int rtc6226_i2c_probe(struct i2c_client *client,
	radio->buffer = kmalloc(radio->buf_size, GFP_KERNEL);
	if (!radio->buffer) {
		retval = -EIO;
		goto err_radio;
		goto err;
	}

	for (i = 0; i < RTC6226_FM_BUF_MAX; i++) {
@@ -618,7 +837,7 @@ static int rtc6226_i2c_probe(struct i2c_client *client,
			pr_err("%s: failed allocating buffers %d\n",
					__func__, kfifo_alloc_rc);
			retval = -ENOMEM;
			goto err;
			goto err_rds;
		}
	}
	radio->wqueue = NULL;
@@ -636,19 +855,19 @@ static int rtc6226_i2c_probe(struct i2c_client *client,
	radio->wqueue  = create_singlethread_workqueue("fmradio");
	if (!radio->wqueue) {
		retval = -ENOMEM;
		goto err_wqueue;
		goto err_rds;
	}

	radio->wqueue_scan  = create_singlethread_workqueue("fmradioscan");
	if (!radio->wqueue_scan) {
		retval = -ENOMEM;
		goto err_wqueue_scan;
		goto err_wqueue;
	}

	radio->wqueue_rds  = create_singlethread_workqueue("fmradiords");
	if (!radio->wqueue_rds) {
		retval = -ENOMEM;
		goto err_all;
		goto err_wqueue_scan;
	}

	/* register video device */
@@ -656,7 +875,7 @@ static int rtc6226_i2c_probe(struct i2c_client *client,
		radio_nr);
	if (retval) {
		dev_info(&client->dev, "Could not register video device\n");
		goto err_rds;
		goto err_all;
	}

	i2c_set_clientdata(client, radio);		/* move from below */
@@ -667,16 +886,28 @@ static int rtc6226_i2c_probe(struct i2c_client *client,
	destroy_workqueue(radio->wqueue_rds);
err_wqueue_scan:
	destroy_workqueue(radio->wqueue_scan);
err_rds:
	kfree(radio->buffer);
err_wqueue:
	destroy_workqueue(radio->wqueue);
err_rds:
	kfree(radio->buffer);
err:
	video_device_release(&radio->videodev);
err_v4l2:
	v4l2_device_unregister(v4l2_dev);
err_radio:
err_vreg:
	if (radio && radio->vioreg && radio->vioreg->reg) {
		regulator_put(radio->vioreg->reg);
		devm_kfree(&client->dev, radio->vioreg);
	} else {
		regulator_put(viovreg);
	}
	if (radio && radio->vddreg && radio->vddreg->reg) {
		regulator_put(radio->vddreg->reg);
		devm_kfree(&client->dev, radio->vddreg);
	} else {
		regulator_put(vddvreg);
	}
	kfree(radio);
err_initial:
	return retval;
}

+16 −0
Original line number Diff line number Diff line
@@ -318,6 +318,8 @@
#define CH_SPACING_200 200
#define CH_SPACING_100 100
#define CH_SPACING_50 50
#define TURNING_ON 1
#define TURNING_OFF 0

#define RW_PRIBASE	(V4L2_CID_USER_BASE | 0xf000)

@@ -441,6 +443,18 @@ struct rtc6226_af_info {
	u32 af_list[MAX_NO_OF_AF];
};

struct fm_power_vreg_data {
	/* voltage regulator handle */
	struct regulator *reg;
	/* regulator name */
	const char *name;
	/* voltage levels to be set */
	unsigned int low_vol_level;
	unsigned int high_vol_level;
	/* is this regulator enabled? */
	bool is_enabled;
};

/*
 * rtc6226_device - private data
 */
@@ -456,6 +470,8 @@ struct rtc6226_device {
	struct pinctrl_state *gpio_state_active;
	struct pinctrl_state *gpio_state_suspend;
	struct v4l2_ctrl_handler ctrl_handler;
	struct fm_power_vreg_data *vddreg;
	struct fm_power_vreg_data *vioreg;
	int band;
	int space;
	unsigned int users;
+3 −0
Original line number Diff line number Diff line
@@ -716,6 +716,9 @@ static void determine_valid_ioctls(struct video_device *vdev)
		SET_VALID_IOCTL(ops, VIDIOC_ENUM_DV_TIMINGS, vidioc_enum_dv_timings);
		SET_VALID_IOCTL(ops, VIDIOC_DV_TIMINGS_CAP, vidioc_dv_timings_cap);
		SET_VALID_IOCTL(ops, VIDIOC_G_EDID, vidioc_g_edid);
	} else {
		/* ioctls valid for radio */
		SET_VALID_IOCTL(ops, VIDIOC_DQBUF, vidioc_dqbuf);
	}
	if (is_tx && (is_radio || is_sdr)) {
		/* radio transmitter only ioctls */
Loading