From: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
To: linux-sh@vger.kernel.org
Subject: Re: [PATCH 1/3] media: i2c: Add ADV761X support
Date: Wed, 25 Sep 2013 10:21:04 +0000 [thread overview]
Message-ID: <1968602.ie7TDIZtkL@avalon> (raw)
In-Reply-To: <1380029916-10331-2-git-send-email-valentine.barshak@cogentembedded.com>
Hi Valentine,
Thank you for the patch.
Please see below for a couple of comments (in addition to Hans' and Guennadi's
comments).
On Tuesday 24 September 2013 17:38:34 Valentine Barshak wrote:
> This adds ADV7611/ADV7612 Dual Port Xpressview
> 225 MHz HDMI Receiver support.
>
> The code is based on the ADV7604 driver, and ADV7612 patch
> by Shinobu Uehara <shinobu.uehara.xc@renesas.com>
>
> Signed-off-by: Valentine Barshak <valentine.barshak@cogentembedded.com>
> ---
> drivers/media/i2c/Kconfig | 11 +
> drivers/media/i2c/Makefile | 1 +
> drivers/media/i2c/adv761x.c | 1060 ++++++++++++++++++++++++++++++++++++++++
> include/media/adv761x.h | 28 ++
> 4 files changed, 1100 insertions(+)
> create mode 100644 drivers/media/i2c/adv761x.c
> create mode 100644 include/media/adv761x.h
[snip]
> diff --git a/drivers/media/i2c/adv761x.c b/drivers/media/i2c/adv761x.c
> new file mode 100644
> index 0000000..bc3dfc3
> --- /dev/null
> +++ b/drivers/media/i2c/adv761x.c
[snip]
> +/* Supported color format list */
> +static const struct adv761x_color_format adv761x_cfmts[] = {
> + {
> + .code = V4L2_MBUS_FMT_RGB888_1X24,
> + .colorspace = V4L2_COLORSPACE_SRGB,
> + },
> +};
Do you plan to add support for more formats later ?
[snip]
> +static inline int edid_write_block(struct v4l2_subdev *sd,
> + unsigned len, const u8 *val)
I would pass a pointer to adv761x_state to the internal functions instead of
getting it from the subdev pointer each time, but that's up to you.
> +{
> + struct i2c_client *client = v4l2_get_subdevdata(sd);
> + struct adv761x_state *state = to_state(sd);
> + int ret = 0;
> + int i;
i is used as an unsigned number, please make it unsigned int. Same comment for
all the locations below where i is used as unsigned.
> +
> + v4l2_dbg(2, debug, sd, "writing EDID block (%d bytes)\n", len);
> +
> + v4l2_subdev_notify(sd, ADV761X_HOTPLUG, (void *)0);
This means that the master V4L2 driver will need to handle ADV761x-specific
events, which doesn't sound very good. What do you use the event for ? Could
you create a standard interface for this ?
> + /* Disable I2C access to internal EDID ram from DDC port */
> + rep_write(sd, 0x74, 0x0);
Could you #define constants for register addresses and values instead of
hardcoding them ?
> + for (i = 0; !ret && i < len; i += I2C_SMBUS_BLOCK_MAX)
> + ret = adv_smbus_write_i2c_block_data(state->i2c_edid, i,
> + I2C_SMBUS_BLOCK_MAX, val + i);
> + if (ret)
> + return ret;
> +
> + /* adv761x calculates the checksums and enables I2C access
> + * to internal EDID ram from DDC port.
> + */
> + rep_write(sd, 0x74, 0x01);
> +
> + for (i = 0; i < 1000; i++) {
> + if (rep_read(sd, 0x76) & 0x1) {
> + /* enable hotplug after 100 ms */
> + queue_delayed_work(state->work_queue,
> + &state->enable_hotplug, HZ / 10);
> + return 0;
> + }
> + schedule();
I haven't checked the datasheet, but can't the chip generate an interrupt that
could replace the busy-loop ?
> + }
> +
> + v4l_err(client, "error enabling edid\n");
> + return -EIO;
> +}
[snip]
> +static int adv761x_set_edid(struct v4l2_subdev *sd,
> + struct v4l2_subdev_edid *edid)
> +{
> + struct adv761x_state *state = to_state(sd);
> + int ret;
> +
> + if (edid->pad != 0)
> + return -EINVAL;
> +
> + if (edid->start_block != 0)
> + return -EINVAL;
> +
> + if (edid->blocks = 0) {
> + /* Pull down the hotplug pin */
> + v4l2_subdev_notify(sd, ADV761X_HOTPLUG, (void *)0);
> + /* Disables I2C access to internal EDID ram from DDC port */
> + rep_write(sd, 0x74, 0x0);
> + state->edid_blocks = 0;
> + return 0;
> + }
> +
> + if (edid->blocks > 2)
> + return -E2BIG;
> +
> + if (!edid->edid)
> + return -EINVAL;
> +
> + memcpy(state->edid, edid->edid, 128 * edid->blocks);
> + state->edid_blocks = edid->blocks;
> +
> + ret = edid_write_block(sd, 128 * edid->blocks, state->edid);
> + if (ret < 0)
> + v4l2_err(sd, "error %d writing edid\n", ret);
Stupid question, what are the use cases for setting EDID on an HDMI receiver ?
Isn't that something that should just be read from the device ?
> +
> + return ret;
> +}
[snip]
> +static int adv761x_try_mbus_fmt(struct v4l2_subdev *sd,
> + struct v4l2_mbus_framefmt *mf)
> +{
> + struct adv761x_state *state = to_state(sd);
> + int i, ret;
> + u8 progressive;
> + u32 width;
> + u32 height;
> +
> + for (i = 0; i < ARRAY_SIZE(adv761x_cfmts); i++) {
> + if (mf->code = adv761x_cfmts[i].code)
> + break;
> + }
> +
> + if (i = ARRAY_SIZE(adv761x_cfmts)) {
> + /* Unsupported format requested. Propose the current one */
> + mf->colorspace = state->cfmt->colorspace;
> + mf->code = state->cfmt->code;
I would propose a fixed default value instead of the current format to make
the driver behaviour more reproducible.
> + } else {
> + /* Also return the colorspace */
> + mf->colorspace = adv761x_cfmts[i].colorspace;
> + }
> +
> + /* Get video information */
> + ret = adv761x_get_vid_info(sd, &progressive, &width, &height);
> + if (ret < 0) {
> + width = ADV761X_MAX_WIDTH;
> + height = ADV761X_MAX_HEIGHT;
> + progressive = 1;
> + }
> +
> + mf->width = width;
> + mf->height = height;
> + mf->field = (progressive) ? V4L2_FIELD_NONE : V4L2_FIELD_INTERLACED;
> +
> + return 0;
> +}
> +
> +static int adv761x_s_mbus_fmt(struct v4l2_subdev *sd,
> + struct v4l2_mbus_framefmt *mf)
> +{
> + struct adv761x_state *state = to_state(sd);
> + int i, ret;
> + u8 progressive;
> + u32 width;
> + u32 height;
> +
> + for (i = 0; i < ARRAY_SIZE(adv761x_cfmts); i++) {
> + if (mf->code = adv761x_cfmts[i].code) {
> + state->cfmt = &adv761x_cfmts[i];
> + break;
> + }
> + }
> + if (i >= ARRAY_SIZE(adv761x_cfmts))
> + return -EINVAL;
You should select a default format instead of returning an error. The format
try and set operations should adjust the requested input parameters, not
return errors.
> +
> + /* Get video information */
> + ret = adv761x_get_vid_info(sd, &progressive, &width, &height);
> + if (ret < 0) {
Is this really a recoverable error that can be ignored silently ?
> + width = ADV761X_MAX_WIDTH;
> + height = ADV761X_MAX_HEIGHT;
> + progressive = 1;
> + }
> +
> + state->width = width;
> + state->height = height;
> + state->scanmode = (progressive) ?
> + V4L2_FIELD_NONE : V4L2_FIELD_INTERLACED;
> +
> + mf->width = state->width;
> + mf->height = state->height;
> + mf->code = state->cfmt->code;
> + mf->field = state->scanmode;
> + mf->colorspace = state->cfmt->colorspace;
> +
> + return 0;
> +}
[snip]
> +static inline int adv761x_check_rev(struct i2c_client *client)
> +{
> + int msb, rev;
> +
> + msb = adv_smbus_read_byte_data(client, 0xea);
> + if (msb < 0)
> + return msb;
> +
> + rev = adv_smbus_read_byte_data(client, 0xeb);
> + if (rev < 0)
> + return rev;
> +
> + rev |= msb << 8;
If you end up removing the retry loops in the read and write functions, don't
forget to switch to smbus_read_word_data() where applicable.
> + switch (rev) {
> + case 0x2051:
> + return 7611;
> + case 0x2041:
> + return 7612;
> + default:
> + break;
> + }
> +
> + return -ENODEV;
> +}
> +
> +static int adv761x_core_init(struct v4l2_subdev *sd)
> +{
> + io_write(sd, 0x01, 0x06); /* V-FREQ = 60Hz */
> + /* Prim_Mode = HDMI-GR */
> + io_write(sd, 0x02, 0xf2); /* Auto CSC, RGB out */
> + /* Disable op_656 bit */
> + io_write(sd, 0x03, 0x42); /* 36 bit SDR 444 Mode 0 */
> + io_write(sd, 0x05, 0x28); /* AV Codes Off */
> + io_write(sd, 0x0b, 0x44); /* Power up part */
> + io_write(sd, 0x0c, 0x42); /* Power up part */
> + io_write(sd, 0x14, 0x7f); /* Max Drive Strength */
> + io_write(sd, 0x15, 0x80); /* Disable Tristate of Pins */
> + /* (Audio output pins active) */
> + io_write(sd, 0x19, 0x83); /* LLC DLL phase */
> + io_write(sd, 0x33, 0x40); /* LLC DLL enable */
> +
> + cp_write(sd, 0xba, 0x01); /* Set HDMI FreeRun */
> + cp_write(sd, 0x3e, 0x80); /* Enable color adjustments */
> +
> + hdmi_write(sd, 0x9b, 0x03); /* ADI recommended setting */
> + hdmi_write(sd, 0x00, 0x08); /* Set HDMI Input Port A */
> + /* (BG_MEAS_PORT_SEL = 001b) */
> + hdmi_write(sd, 0x02, 0x03); /* Enable Ports A & B in */
> + /* background mode */
> + hdmi_write(sd, 0x6d, 0x80); /* Enable TDM mode */
> + hdmi_write(sd, 0x03, 0x18); /* I2C mode 24 bits */
> + hdmi_write(sd, 0x83, 0xfc); /* Enable clock terminators */
> + /* for port A & B */
> + hdmi_write(sd, 0x6f, 0x0c); /* ADI recommended setting */
> + hdmi_write(sd, 0x85, 0x1f); /* ADI recommended setting */
> + hdmi_write(sd, 0x87, 0x70); /* ADI recommended setting */
> + hdmi_write(sd, 0x8d, 0x04); /* LFG Port A */
> + hdmi_write(sd, 0x8e, 0x1e); /* HFG Port A */
> + hdmi_write(sd, 0x1a, 0x8a); /* unmute audio */
> + hdmi_write(sd, 0x57, 0xda); /* ADI recommended setting */
> + hdmi_write(sd, 0x58, 0x01); /* ADI recommended setting */
> + hdmi_write(sd, 0x75, 0x10); /* DDC drive strength */
> + hdmi_write(sd, 0x90, 0x04); /* LFG Port B */
> + hdmi_write(sd, 0x91, 0x1e); /* HFG Port B */
> + hdmi_write(sd, 0x04, 0x03);
> + hdmi_write(sd, 0x14, 0x00);
> + hdmi_write(sd, 0x15, 0x00);
> + hdmi_write(sd, 0x16, 0x00);
Don't you need to check for errors ? You should in that case put the default
values in an array to simplify the code.
> + rep_write(sd, 0x40, 0x81); /* Disable HDCP 1.1 features */
> + rep_write(sd, 0x74, 0x00); /* Disable the Internal EDID */
> + /* for all ports */
> +
> + return v4l2_ctrl_handler_setup(sd->ctrl_handler);
> +}
[snip]
> +static int adv761x_resume(struct device *dev)
> +{
> + struct i2c_client *client = to_i2c_client(dev);
> + struct v4l2_subdev *sd = i2c_get_clientdata(client);
> +
> + /* Initializes ADV761X to its default values */
> + return adv761x_core_init(sd);
Don't you also need to restore the sub-devices I2C addresses here ?
> +}
[snip]
--
Regards,
Laurent Pinchart
next prev parent reply other threads:[~2013-09-25 10:21 UTC|newest]
Thread overview: 23+ messages / expand[flat|nested] mbox.gz Atom feed top
2013-09-24 13:38 [PATCH 1/3] media: i2c: Add ADV761X support Valentine Barshak
2013-09-24 14:17 ` Hans Verkuil
2013-09-24 15:54 ` Guennadi Liakhovetski
2013-09-24 16:19 ` Guennadi Liakhovetski
2013-09-24 17:37 ` Hans Verkuil
2013-09-24 18:09 ` Andy Walls
2013-09-25 9:57 ` Laurent Pinchart
2013-09-25 10:21 ` Laurent Pinchart [this message]
2013-09-25 12:33 ` Valentine
2013-09-25 13:02 ` Valentine
2013-09-25 14:08 ` Guennadi Liakhovetski
2013-09-25 15:16 ` Valentine
2013-09-25 16:31 ` Guennadi Liakhovetski
2013-09-25 17:19 ` Valentine
2013-09-25 18:33 ` Guennadi Liakhovetski
2013-09-25 20:36 ` Valentine
2013-09-25 22:04 ` Laurent Pinchart
2013-09-25 22:57 ` Laurent Pinchart
2013-09-26 6:31 ` Hans Verkuil
2013-09-26 6:45 ` Hans Verkuil
2013-09-26 6:57 ` Hans Verkuil
2013-09-26 9:36 ` Laurent Pinchart
2013-09-26 9:39 ` Laurent Pinchart
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1968602.ie7TDIZtkL@avalon \
--to=laurent.pinchart@ideasonboard.com \
--cc=linux-sh@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox