RE: [PATCH 2/5] media: i2c: max2175: Add MAX2175 support

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi Hans,

Thank you for the review comments.

> On 11/09/2016 04:44 PM, Ramesh Shanmugasundaram wrote:
> > This patch adds driver support for MAX2175 chip. This is Maxim
> > Integrated's RF to Bits tuner front end chip designed for
> > software-defined radio solutions. This driver exposes the tuner as a
> > sub-device instance with standard and custom controls to configure the
> device.
> >
> > Signed-off-by: Ramesh Shanmugasundaram
> > <ramesh.shanmugasundaram@xxxxxxxxxxxxxx>
> > ---
> >  .../devicetree/bindings/media/i2c/max2175.txt      |   61 +
> >  drivers/media/i2c/Kconfig                          |    4 +
> >  drivers/media/i2c/Makefile                         |    2 +
> >  drivers/media/i2c/max2175/Kconfig                  |    8 +
> >  drivers/media/i2c/max2175/Makefile                 |    4 +
> >  drivers/media/i2c/max2175/max2175.c                | 1558
> ++++++++++++++++++++
> >  drivers/media/i2c/max2175/max2175.h                |  108 ++
> >  7 files changed, 1745 insertions(+)
> >  create mode 100644
> > Documentation/devicetree/bindings/media/i2c/max2175.txt
> >  create mode 100644 drivers/media/i2c/max2175/Kconfig  create mode
> > 100644 drivers/media/i2c/max2175/Makefile
> >  create mode 100644 drivers/media/i2c/max2175/max2175.c
> >  create mode 100644 drivers/media/i2c/max2175/max2175.h
> >
> 
> <snip>
> 
> > diff --git a/drivers/media/i2c/max2175/max2175.c
> > b/drivers/media/i2c/max2175/max2175.c
> > new file mode 100644
> > index 0000000..ec45b52
> > --- /dev/null
> > +++ b/drivers/media/i2c/max2175/max2175.c
> > @@ -0,0 +1,1558 @@
> 
> <snip>
> 
> > +/* Read/Write bit(s) on top of regmap */ static int
> > +max2175_read(struct max2175 *ctx, u8 idx, u8 *val) {
> > +	u32 regval;
> > +	int ret = regmap_read(ctx->regmap, idx, &regval);
> > +
> > +	if (ret)
> > +		v4l2_err(ctx->client, "read ret(%d): idx 0x%02x\n", ret, idx);
> > +
> > +	*val = regval;
> 
> Does regmap_read initialize regval even if it returns an error? If not,
> then I would initialize regval to 0 to prevent *val being uninitialized.

Agreed.

> 
> > +	return ret;
> > +}
> > +
> > +static int max2175_write(struct max2175 *ctx, u8 idx, u8 val) {
> > +	int ret = regmap_write(ctx->regmap, idx, val);
> > +
> > +	if (ret)
> > +		v4l2_err(ctx->client, "write ret(%d): idx 0x%02x val
> 0x%02x\n",
> > +			 ret, idx, val);
> > +	return ret;
> > +}
> > +
> > +static u8 max2175_read_bits(struct max2175 *ctx, u8 idx, u8 msb, u8
> > +lsb) {
> > +	u8 val;
> > +
> > +	if (max2175_read(ctx, idx, &val))
> > +		return 0;
> > +
> > +	return max2175_get_bitval(val, msb, lsb); }
> > +
> > +static bool max2175_read_bit(struct max2175 *ctx, u8 idx, u8 bit) {
> > +	return !!max2175_read_bits(ctx, idx, bit, bit); }
> > +
> > +static int max2175_write_bits(struct max2175 *ctx, u8 idx,
> > +			     u8 msb, u8 lsb, u8 newval)
> > +{
> > +	int ret = regmap_update_bits(ctx->regmap, idx, GENMASK(msb, lsb),
> > +				     newval << lsb);
> > +
> > +	if (ret)
> > +		v4l2_err(ctx->client, "wbits ret(%d): idx 0x%02x\n", ret,
> idx);
> > +
> > +	return ret;
> > +}
> > +
> > +static int max2175_write_bit(struct max2175 *ctx, u8 idx, u8 bit, u8
> > +newval) {
> > +	return max2175_write_bits(ctx, idx, bit, bit, newval); }
> > +
> > +/* Checks expected pattern every msec until timeout */ static int
> > +max2175_poll_timeout(struct max2175 *ctx, u8 idx, u8 msb, u8 lsb,
> > +				u8 exp_bitval, u32 timeout_ms)
> > +{
> > +	unsigned int val;
> > +
> > +	return regmap_read_poll_timeout(ctx->regmap, idx, val,
> > +			(max2175_get_bitval(val, msb, lsb) == exp_bitval),
> > +			1000, timeout_ms * 1000);
> > +}
> > +
> > +static int max2175_poll_csm_ready(struct max2175 *ctx) {
> > +	int ret;
> > +
> > +	ret = max2175_poll_timeout(ctx, 69, 1, 1, 0, 50);
> > +	if (ret)
> > +		v4l2_err(ctx->client, "csm not ready\n");
> > +
> > +	return ret;
> > +}
> > +
> > +#define MAX2175_IS_BAND_AM(ctx)		\
> > +	(max2175_read_bits(ctx, 5, 1, 0) == MAX2175_BAND_AM)
> > +
> > +#define MAX2175_IS_BAND_VHF(ctx)	\
> > +	(max2175_read_bits(ctx, 5, 1, 0) == MAX2175_BAND_VHF)
> > +
> > +#define MAX2175_IS_FM_MODE(ctx)		\
> > +	(max2175_read_bits(ctx, 12, 5, 4) == 0)
> > +
> > +#define MAX2175_IS_FMHD_MODE(ctx)	\
> > +	(max2175_read_bits(ctx, 12, 5, 4) == 1)
> > +
> > +#define MAX2175_IS_DAB_MODE(ctx)	\
> > +	(max2175_read_bits(ctx, 12, 5, 4) == 2)
> > +
> > +static int max2175_band_from_freq(u32 freq) {
> > +	if (freq >= 144000 && freq <= 26100000)
> > +		return MAX2175_BAND_AM;
> > +	else if (freq >= 65000000 && freq <= 108000000)
> > +		return MAX2175_BAND_FM;
> > +	else
> 
> No need for these 'else' keywords.

Agreed.

> 
> > +		return MAX2175_BAND_VHF;
> > +}
> > +
> > +static int max2175_update_i2s_mode(struct max2175 *ctx, u32 rx_mode,
> > +				   u32 i2s_mode)
> > +{
> > +	max2175_write_bits(ctx, 29, 2, 0, i2s_mode);
> > +
> > +	/* Based on I2S mode value I2S_WORD_CNT values change */
> > +	switch (i2s_mode) {
> > +	case MAX2175_I2S_MODE3:
> > +		max2175_write_bits(ctx, 30, 6, 0, 1);
> > +		break;
> > +	case MAX2175_I2S_MODE2:
> > +	case MAX2175_I2S_MODE4:
> > +		max2175_write_bits(ctx, 30, 6, 0, 0);
> > +		break;
> > +	case MAX2175_I2S_MODE0:
> > +		max2175_write_bits(ctx, 30, 6, 0,
> > +			ctx->rx_modes[rx_mode].i2s_word_size);
> > +		break;
> > +	}
> > +	mxm_dbg(ctx, "update_i2s_mode %u, rx_mode %u\n", i2s_mode, rx_mode);
> > +	return 0;
> > +}

[snip]

> > +
> > +static int max2175_enum_freq_bands(struct v4l2_subdev *sd,
> > +			    struct v4l2_frequency_band *band) {
> > +	struct max2175 *ctx = max2175_from_sd(sd);
> > +
> > +	if (band->tuner == 0 && band->index == 0)
> > +		*band = *ctx->bands_rf;
> > +	else
> > +		return -EINVAL;
> 
> This is a bit ugly. I would invert the condition and return -EINVAL.
> Then assign *band and return 0.

Agreed.

> 
> > +
> > +	return 0;
> > +}
> > +
> > +static int max2175_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner
> > +*vt) {
> > +	struct max2175 *ctx = max2175_from_sd(sd);
> > +
> > +	if (vt->index > 0)
> > +		return -EINVAL;
> > +
> > +	strlcpy(vt->name, "RF", sizeof(vt->name));
> > +	vt->type = V4L2_TUNER_RF;
> > +	vt->capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS;
> > +	vt->rangelow = ctx->bands_rf->rangelow;
> > +	vt->rangehigh = ctx->bands_rf->rangehigh;
> > +	return 0;
> > +}
> > +
> > +static int max2175_s_tuner(struct v4l2_subdev *sd, const struct
> > +v4l2_tuner *vt) {
> > +	/* Check tuner index is valid */
> > +	if (vt->index > 0)
> > +		return -EINVAL;
> > +
> > +	return 0;
> > +}
> > +
> > +static const struct v4l2_subdev_tuner_ops max2175_tuner_ops = {
> > +	.s_frequency = max2175_s_frequency,
> > +	.g_frequency = max2175_g_frequency,
> > +	.enum_freq_bands = max2175_enum_freq_bands,
> > +	.g_tuner = max2175_g_tuner,
> > +	.s_tuner = max2175_s_tuner,
> > +};
> > +
> > +static const struct v4l2_subdev_ops max2175_ops = {
> > +	.tuner = &max2175_tuner_ops,
> > +};
> > +
> > +static const struct v4l2_ctrl_ops max2175_ctrl_ops = {
> > +	.s_ctrl = max2175_s_ctrl,
> > +	.g_volatile_ctrl = max2175_g_volatile_ctrl, };
> > +
> > +static const struct v4l2_ctrl_config max2175_i2s_en = {
> > +	.ops = &max2175_ctrl_ops,
> > +	.id = V4L2_CID_MAX2175_I2S_ENABLE,
> > +	.name = "I2S Enable",
> > +	.type = V4L2_CTRL_TYPE_BOOLEAN,
> > +	.min = 0,
> > +	.max = 1,
> > +	.step = 1,
> > +	.def = 1,
> > +};
> > +
> > +static const char * const max2175_ctrl_i2s_modes[] = {
> > +	[MAX2175_I2S_MODE0]	= "i2s mode 0",
> > +	[MAX2175_I2S_MODE1]	= "i2s mode 1 (skipped)",
> > +	[MAX2175_I2S_MODE2]	= "i2s mode 2",
> > +	[MAX2175_I2S_MODE3]	= "i2s mode 3",
> > +	[MAX2175_I2S_MODE4]	= "i2s mode 4",
> > +};
> > +
> > +static const struct v4l2_ctrl_config max2175_i2s_mode = {
> > +	.ops = &max2175_ctrl_ops,
> > +	.id = V4L2_CID_MAX2175_I2S_MODE,
> > +	.name = "I2S MODE value",
> > +	.type = V4L2_CTRL_TYPE_MENU,
> > +	.max = ARRAY_SIZE(max2175_ctrl_i2s_modes) - 1,
> > +	.def = 0,
> > +	.menu_skip_mask = 0x02,
> > +	.qmenu = max2175_ctrl_i2s_modes,
> > +};
> 
> Is this something that is changed dynamically? It looks more like a device
> tree thing (it's not clear what it does, so obviously I can't be sure).

Yes. It can be changed dynamically. 

> 
> > +
> > +static const struct v4l2_ctrl_config max2175_hsls = {
> > +	.ops = &max2175_ctrl_ops,
> > +	.id = V4L2_CID_MAX2175_HSLS,
> > +	.name = "HSLS above/below desired",
> > +	.type = V4L2_CTRL_TYPE_INTEGER,
> > +	.min = 0,
> > +	.max = 1,
> > +	.step = 1,
> > +	.def = 1,
> > +};
> > +
> > +static const char * const max2175_ctrl_eu_rx_modes[] = {
> > +	[MAX2175_EU_FM_1_2]	= "EU FM 1.2",
> > +	[MAX2175_DAB_1_2]	= "DAB 1.2",
> > +};
> > +
> > +static const char * const max2175_ctrl_na_rx_modes[] = {
> > +	[MAX2175_NA_FM_1_0]	= "NA FM 1.0",
> > +	[MAX2175_NA_FM_2_0]	= "NA FM 2.0",
> > +};
> > +
> > +static const struct v4l2_ctrl_config max2175_eu_rx_mode = {
> > +	.ops = &max2175_ctrl_ops,
> > +	.id = V4L2_CID_MAX2175_RX_MODE,
> > +	.name = "RX MODE",
> > +	.type = V4L2_CTRL_TYPE_MENU,
> > +	.max = ARRAY_SIZE(max2175_ctrl_eu_rx_modes) - 1,
> > +	.def = 0,
> > +	.qmenu = max2175_ctrl_eu_rx_modes,
> > +};
> > +
> > +static const struct v4l2_ctrl_config max2175_na_rx_mode = {
> > +	.ops = &max2175_ctrl_ops,
> > +	.id = V4L2_CID_MAX2175_RX_MODE,
> > +	.name = "RX MODE",
> > +	.type = V4L2_CTRL_TYPE_MENU,
> > +	.max = ARRAY_SIZE(max2175_ctrl_na_rx_modes) - 1,
> > +	.def = 0,
> > +	.qmenu = max2175_ctrl_na_rx_modes,
> > +};
> 
> Please document all these controls better. This is part of the public API,
> so you need to give more information what this means exactly.

Thanks. Now, I have added a one-liner and a bit descriptive explanation at Documentation/media/v4l-drivers dir as you & Laurent concluded.

Thanks,
Ramesh
��.n��������+%������w��{.n�����{��g����^n�r������&��z�ޗ�zf���h���~����������_��+v���)ߣ�

[Index of Archives]     [Linux Input]     [Video for Linux]     [Gstreamer Embedded]     [Mplayer Users]     [Linux USB Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Yosemite Backpacking]
  Powered by Linux