OSDN Git Service

media: adv7842: support EDIDs up to 4 blocks
authorHans Verkuil <hverkuil-cisco@xs4all.nl>
Thu, 6 May 2021 12:06:57 +0000 (14:06 +0200)
committerMauro Carvalho Chehab <mchehab+huawei@kernel.org>
Wed, 2 Jun 2021 11:16:01 +0000 (13:16 +0200)
The adv7842 driver didn't support EDIDs of 3 or 4 blocks, even though the
hardware supports this.

It is a bit more complicated due to the fact that the adv7842 can expose
two EDIDs: one digital, one analog, for DVI-I connectors. In that case the
VGA_EDID_ENABLE bit is set and blocks 0 and 1 of the EDID eeprom are used
for the DVI-D part and block 2 is used for the DVI-A part of the DVI-I
connector.

Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
drivers/media/i2c/adv7842.c

index ff10af7..78e61fe 100644 (file)
@@ -98,12 +98,12 @@ struct adv7842_state {
 
        v4l2_std_id norm;
        struct {
-               u8 edid[256];
+               u8 edid[512];
                u32 blocks;
                u32 present;
        } hdmi_edid;
        struct {
-               u8 edid[256];
+               u8 edid[128];
                u32 blocks;
                u32 present;
        } vga_edid;
@@ -720,6 +720,9 @@ static int edid_write_vga_segment(struct v4l2_subdev *sd)
 
        v4l2_dbg(2, debug, sd, "%s: write EDID on VGA port\n", __func__);
 
+       if (!state->vga_edid.present)
+               return 0;
+
        /* HPA disable on port A and B */
        io_write_and_or(sd, 0x20, 0xcf, 0x00);
 
@@ -763,7 +766,7 @@ static int edid_write_hdmi_segment(struct v4l2_subdev *sd, u8 port)
        struct adv7842_state *state = to_state(sd);
        const u8 *edid = state->hdmi_edid.edid;
        u32 blocks = state->hdmi_edid.blocks;
-       int spa_loc;
+       unsigned int spa_loc;
        u16 pa, parent_pa;
        int err = 0;
        int i;
@@ -796,12 +799,14 @@ static int edid_write_hdmi_segment(struct v4l2_subdev *sd, u8 port)
                pa = (edid[spa_loc] << 8) | edid[spa_loc + 1];
        }
 
-       /* edid segment pointer '0' for HDMI ports */
-       rep_write_and_or(sd, 0x77, 0xef, 0x00);
 
-       for (i = 0; !err && i < blocks * 128; i += I2C_SMBUS_BLOCK_MAX)
+       for (i = 0; !err && i < blocks * 128; i += I2C_SMBUS_BLOCK_MAX) {
+               /* set edid segment pointer for HDMI ports */
+               if (i % 256 == 0)
+                       rep_write_and_or(sd, 0x77, 0xef, i >= 256 ? 0x10 : 0x00);
                err = i2c_smbus_write_i2c_block_data(state->i2c_edid, i,
                                                     I2C_SMBUS_BLOCK_MAX, edid + i);
+       }
        if (err)
                return err;
 
@@ -2491,9 +2496,17 @@ static int adv7842_get_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid)
        return 0;
 }
 
+/*
+ * If the VGA_EDID_ENABLE bit is set (Repeater Map 0x7f, bit 7), then
+ * the first two blocks of the EDID are for the HDMI, and the first block
+ * of segment 1 (i.e. the third block of the EDID) is for VGA.
+ * So if a VGA EDID is installed, then the maximum size of the HDMI EDID
+ * is 2 blocks.
+ */
 static int adv7842_set_edid(struct v4l2_subdev *sd, struct v4l2_edid *e)
 {
        struct adv7842_state *state = to_state(sd);
+       unsigned int max_blocks = e->pad == ADV7842_EDID_PORT_VGA ? 1 : 4;
        int err = 0;
 
        memset(e->reserved, 0, sizeof(e->reserved));
@@ -2502,8 +2515,12 @@ static int adv7842_set_edid(struct v4l2_subdev *sd, struct v4l2_edid *e)
                return -EINVAL;
        if (e->start_block != 0)
                return -EINVAL;
-       if (e->blocks > 2) {
-               e->blocks = 2;
+       if (e->pad < ADV7842_EDID_PORT_VGA && state->vga_edid.blocks)
+               max_blocks = 2;
+       if (e->pad == ADV7842_EDID_PORT_VGA && state->hdmi_edid.blocks > 2)
+               return -EBUSY;
+       if (e->blocks > max_blocks) {
+               e->blocks = max_blocks;
                return -E2BIG;
        }
 
@@ -2514,7 +2531,7 @@ static int adv7842_set_edid(struct v4l2_subdev *sd, struct v4l2_edid *e)
 
        switch (e->pad) {
        case ADV7842_EDID_PORT_VGA:
-               memset(&state->vga_edid.edid, 0, 256);
+               memset(&state->vga_edid.edid, 0, sizeof(state->vga_edid.edid));
                state->vga_edid.blocks = e->blocks;
                state->vga_edid.present = e->blocks ? 0x1 : 0x0;
                if (e->blocks)
@@ -2523,7 +2540,7 @@ static int adv7842_set_edid(struct v4l2_subdev *sd, struct v4l2_edid *e)
                break;
        case ADV7842_EDID_PORT_A:
        case ADV7842_EDID_PORT_B:
-               memset(&state->hdmi_edid.edid, 0, 256);
+               memset(&state->hdmi_edid.edid, 0, sizeof(state->hdmi_edid.edid));
                state->hdmi_edid.blocks = e->blocks;
                if (e->blocks) {
                        state->hdmi_edid.present |= 0x04 << e->pad;