Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

media: adv7842: support 1 block EDIDs, fix clearing EDID

Add support for EDIDs consisting of one EDID block.

Related to this, improve CEC physical address handling.

Clearing the EDID caused a bug since v4l2_calc_aspect_ratio() was
called with a NULL pointer.

Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>

authored by

Hans Verkuil and committed by
Mauro Carvalho Chehab
3e057b8a e0a4205d

+43 -28
+43 -28
drivers/media/i2c/adv7842.c
··· 99 99 v4l2_std_id norm; 100 100 struct { 101 101 u8 edid[256]; 102 + u32 blocks; 102 103 u32 present; 103 104 } hdmi_edid; 104 105 struct { 105 106 u8 edid[256]; 107 + u32 blocks; 106 108 u32 present; 107 109 } vga_edid; 108 110 struct v4l2_fract aspect_ratio; ··· 713 711 { 714 712 struct i2c_client *client = v4l2_get_subdevdata(sd); 715 713 struct adv7842_state *state = to_state(sd); 716 - const u8 *val = state->vga_edid.edid; 714 + const u8 *edid = state->vga_edid.edid; 715 + u32 blocks = state->vga_edid.blocks; 717 716 int err = 0; 718 717 int i; 719 718 ··· 729 726 /* edid segment pointer '1' for VGA port */ 730 727 rep_write_and_or(sd, 0x77, 0xef, 0x10); 731 728 732 - for (i = 0; !err && i < 256; i += I2C_SMBUS_BLOCK_MAX) 729 + for (i = 0; !err && i < blocks * 128; i += I2C_SMBUS_BLOCK_MAX) 733 730 err = i2c_smbus_write_i2c_block_data(state->i2c_edid, i, 734 731 I2C_SMBUS_BLOCK_MAX, 735 - val + i); 732 + edid + i); 736 733 if (err) 737 734 return err; 738 735 ··· 762 759 struct i2c_client *client = v4l2_get_subdevdata(sd); 763 760 struct adv7842_state *state = to_state(sd); 764 761 const u8 *edid = state->hdmi_edid.edid; 762 + u32 blocks = state->hdmi_edid.blocks; 765 763 int spa_loc; 766 - u16 pa; 764 + u16 pa, parent_pa; 767 765 int err = 0; 768 766 int i; 769 767 ··· 782 778 return 0; 783 779 } 784 780 785 - pa = v4l2_get_edid_phys_addr(edid, 256, &spa_loc); 786 - err = v4l2_phys_addr_validate(pa, &pa, NULL); 781 + pa = v4l2_get_edid_phys_addr(edid, blocks * 128, &spa_loc); 782 + err = v4l2_phys_addr_validate(pa, &parent_pa, NULL); 787 783 if (err) 788 784 return err; 789 785 790 - /* 791 - * Return an error if no location of the source physical address 792 - * was found. 793 - */ 794 - if (spa_loc == 0) 795 - return -EINVAL; 786 + if (!spa_loc) { 787 + /* 788 + * There is no SPA, so just set spa_loc to 128 and pa to whatever 789 + * data is there. 790 + */ 791 + spa_loc = 128; 792 + pa = (edid[spa_loc] << 8) | edid[spa_loc + 1]; 793 + } 796 794 797 795 /* edid segment pointer '0' for HDMI ports */ 798 796 rep_write_and_or(sd, 0x77, 0xef, 0x00); 799 797 800 - for (i = 0; !err && i < 256; i += I2C_SMBUS_BLOCK_MAX) 798 + for (i = 0; !err && i < blocks * 128; i += I2C_SMBUS_BLOCK_MAX) 801 799 err = i2c_smbus_write_i2c_block_data(state->i2c_edid, i, 802 800 I2C_SMBUS_BLOCK_MAX, edid + i); 803 801 if (err) 804 802 return err; 805 803 806 804 if (port == ADV7842_EDID_PORT_A) { 807 - rep_write(sd, 0x72, edid[spa_loc]); 808 - rep_write(sd, 0x73, edid[spa_loc + 1]); 805 + rep_write(sd, 0x72, pa >> 8); 806 + rep_write(sd, 0x73, pa & 0xff); 809 807 } else { 810 - rep_write(sd, 0x74, edid[spa_loc]); 811 - rep_write(sd, 0x75, edid[spa_loc + 1]); 808 + rep_write(sd, 0x74, pa >> 8); 809 + rep_write(sd, 0x75, pa & 0xff); 812 810 } 813 811 rep_write(sd, 0x76, spa_loc & 0xff); 814 812 rep_write_and_or(sd, 0x77, 0xbf, (spa_loc >> 2) & 0x40); ··· 830 824 (port == ADV7842_EDID_PORT_A) ? 'A' : 'B'); 831 825 return -EIO; 832 826 } 833 - cec_s_phys_addr(state->cec_adap, pa, false); 827 + cec_s_phys_addr(state->cec_adap, parent_pa, false); 834 828 835 829 /* enable hotplug after 200 ms */ 836 830 schedule_delayed_work(&state->delayed_work_enable_hotplug, HZ / 5); ··· 2449 2443 static int adv7842_get_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid) 2450 2444 { 2451 2445 struct adv7842_state *state = to_state(sd); 2446 + u32 blocks = 0; 2452 2447 u8 *data = NULL; 2453 2448 2454 2449 memset(edid->reserved, 0, sizeof(edid->reserved)); ··· 2457 2450 switch (edid->pad) { 2458 2451 case ADV7842_EDID_PORT_A: 2459 2452 case ADV7842_EDID_PORT_B: 2460 - if (state->hdmi_edid.present & (0x04 << edid->pad)) 2453 + if (state->hdmi_edid.present & (0x04 << edid->pad)) { 2461 2454 data = state->hdmi_edid.edid; 2455 + blocks = state->hdmi_edid.blocks; 2456 + } 2462 2457 break; 2463 2458 case ADV7842_EDID_PORT_VGA: 2464 - if (state->vga_edid.present) 2459 + if (state->vga_edid.present) { 2465 2460 data = state->vga_edid.edid; 2461 + blocks = state->vga_edid.blocks; 2462 + } 2466 2463 break; 2467 2464 default: 2468 2465 return -EINVAL; 2469 2466 } 2470 2467 2471 2468 if (edid->start_block == 0 && edid->blocks == 0) { 2472 - edid->blocks = data ? 2 : 0; 2469 + edid->blocks = blocks; 2473 2470 return 0; 2474 2471 } 2475 2472 2476 2473 if (!data) 2477 2474 return -ENODATA; 2478 2475 2479 - if (edid->start_block >= 2) 2476 + if (edid->start_block >= blocks) 2480 2477 return -EINVAL; 2481 2478 2482 - if (edid->start_block + edid->blocks > 2) 2483 - edid->blocks = 2 - edid->start_block; 2479 + if (edid->start_block + edid->blocks > blocks) 2480 + edid->blocks = blocks - edid->start_block; 2484 2481 2485 2482 memcpy(edid->edid, data + edid->start_block * 128, edid->blocks * 128); 2486 2483 ··· 2508 2497 } 2509 2498 2510 2499 /* todo, per edid */ 2511 - state->aspect_ratio = v4l2_calc_aspect_ratio(e->edid[0x15], 2512 - e->edid[0x16]); 2500 + if (e->blocks) 2501 + state->aspect_ratio = v4l2_calc_aspect_ratio(e->edid[0x15], 2502 + e->edid[0x16]); 2513 2503 2514 2504 switch (e->pad) { 2515 2505 case ADV7842_EDID_PORT_VGA: 2516 2506 memset(&state->vga_edid.edid, 0, 256); 2507 + state->vga_edid.blocks = e->blocks; 2517 2508 state->vga_edid.present = e->blocks ? 0x1 : 0x0; 2518 - memcpy(&state->vga_edid.edid, e->edid, 128 * e->blocks); 2509 + if (e->blocks) 2510 + memcpy(&state->vga_edid.edid, e->edid, 128 * e->blocks); 2519 2511 err = edid_write_vga_segment(sd); 2520 2512 break; 2521 2513 case ADV7842_EDID_PORT_A: 2522 2514 case ADV7842_EDID_PORT_B: 2523 2515 memset(&state->hdmi_edid.edid, 0, 256); 2516 + state->hdmi_edid.blocks = e->blocks; 2524 2517 if (e->blocks) { 2525 2518 state->hdmi_edid.present |= 0x04 << e->pad; 2519 + memcpy(&state->hdmi_edid.edid, e->edid, 128 * e->blocks); 2526 2520 } else { 2527 2521 state->hdmi_edid.present &= ~(0x04 << e->pad); 2528 2522 adv7842_s_detect_tx_5v_ctrl(sd); 2529 2523 } 2530 - memcpy(&state->hdmi_edid.edid, e->edid, 128 * e->blocks); 2531 2524 err = edid_write_hdmi_segment(sd, e->pad); 2532 2525 break; 2533 2526 default: