diff options
author | Mats Randgaard <matrandg@cisco.com> | 2013-12-10 07:57:09 -0500 |
---|---|---|
committer | Mauro Carvalho Chehab <m.chehab@samsung.com> | 2014-01-07 03:06:14 -0500 |
commit | dd08beb905cc170c596306661ddbe311765c771b (patch) | |
tree | 650a8f68831a901774a1babeb165528efc67b09a | |
parent | 3e86aa856d0b1f9d24c2dd83f4e31f93795f34c6 (diff) |
[media] adv7604: improve EDID handling
- split edid_write_block()
- do not use edid->edid before the validity check
- Return -EINVAL if edid->pad is invalid
- Save both registers for SPA port A
- Set SPA location to default value if it is not found
Signed-off-by: Mats Randgaard <matrandg@cisco.com>
Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
Signed-off-by: Mauro Carvalho Chehab <m.chehab@samsung.com>
-rw-r--r-- | drivers/media/i2c/adv7604.c | 94 |
1 files changed, 50 insertions, 44 deletions
diff --git a/drivers/media/i2c/adv7604.c b/drivers/media/i2c/adv7604.c index d8061c5c30dd..501f40f5024f 100644 --- a/drivers/media/i2c/adv7604.c +++ b/drivers/media/i2c/adv7604.c | |||
@@ -72,7 +72,7 @@ struct adv7604_state { | |||
72 | u32 present; | 72 | u32 present; |
73 | unsigned blocks; | 73 | unsigned blocks; |
74 | } edid; | 74 | } edid; |
75 | u16 spa_port_a; | 75 | u16 spa_port_a[2]; |
76 | struct v4l2_fract aspect_ratio; | 76 | struct v4l2_fract aspect_ratio; |
77 | u32 rgb_quantization_range; | 77 | u32 rgb_quantization_range; |
78 | struct workqueue_struct *work_queues; | 78 | struct workqueue_struct *work_queues; |
@@ -510,22 +510,9 @@ static inline int edid_read_block(struct v4l2_subdev *sd, unsigned len, u8 *val) | |||
510 | return 0; | 510 | return 0; |
511 | } | 511 | } |
512 | 512 | ||
513 | static void adv7604_delayed_work_enable_hotplug(struct work_struct *work) | ||
514 | { | ||
515 | struct delayed_work *dwork = to_delayed_work(work); | ||
516 | struct adv7604_state *state = container_of(dwork, struct adv7604_state, | ||
517 | delayed_work_enable_hotplug); | ||
518 | struct v4l2_subdev *sd = &state->sd; | ||
519 | |||
520 | v4l2_dbg(2, debug, sd, "%s: enable hotplug\n", __func__); | ||
521 | |||
522 | v4l2_subdev_notify(sd, ADV7604_HOTPLUG, (void *)&state->edid.present); | ||
523 | } | ||
524 | |||
525 | static inline int edid_write_block(struct v4l2_subdev *sd, | 513 | static inline int edid_write_block(struct v4l2_subdev *sd, |
526 | unsigned len, const u8 *val) | 514 | unsigned len, const u8 *val) |
527 | { | 515 | { |
528 | struct i2c_client *client = v4l2_get_subdevdata(sd); | ||
529 | struct adv7604_state *state = to_state(sd); | 516 | struct adv7604_state *state = to_state(sd); |
530 | int err = 0; | 517 | int err = 0; |
531 | int i; | 518 | int i; |
@@ -535,24 +522,19 @@ static inline int edid_write_block(struct v4l2_subdev *sd, | |||
535 | for (i = 0; !err && i < len; i += I2C_SMBUS_BLOCK_MAX) | 522 | for (i = 0; !err && i < len; i += I2C_SMBUS_BLOCK_MAX) |
536 | err = adv_smbus_write_i2c_block_data(state->i2c_edid, i, | 523 | err = adv_smbus_write_i2c_block_data(state->i2c_edid, i, |
537 | I2C_SMBUS_BLOCK_MAX, val + i); | 524 | I2C_SMBUS_BLOCK_MAX, val + i); |
538 | if (err) | 525 | return err; |
539 | return err; | 526 | } |
540 | 527 | ||
541 | /* adv7604 calculates the checksums and enables I2C access to internal | 528 | static void adv7604_delayed_work_enable_hotplug(struct work_struct *work) |
542 | EDID RAM from DDC port. */ | 529 | { |
543 | rep_write_and_or(sd, 0x77, 0xf0, state->edid.present); | 530 | struct delayed_work *dwork = to_delayed_work(work); |
531 | struct adv7604_state *state = container_of(dwork, struct adv7604_state, | ||
532 | delayed_work_enable_hotplug); | ||
533 | struct v4l2_subdev *sd = &state->sd; | ||
544 | 534 | ||
545 | for (i = 0; i < 1000; i++) { | 535 | v4l2_dbg(2, debug, sd, "%s: enable hotplug\n", __func__); |
546 | if (rep_read(sd, 0x7d) & state->edid.present) | ||
547 | break; | ||
548 | mdelay(1); | ||
549 | } | ||
550 | if (i == 1000) { | ||
551 | v4l_err(client, "error enabling edid (0x%x)\n", state->edid.present); | ||
552 | return -EIO; | ||
553 | } | ||
554 | 536 | ||
555 | return 0; | 537 | v4l2_subdev_notify(sd, ADV7604_HOTPLUG, (void *)&state->edid.present); |
556 | } | 538 | } |
557 | 539 | ||
558 | static inline int hdmi_read(struct v4l2_subdev *sd, u8 reg) | 540 | static inline int hdmi_read(struct v4l2_subdev *sd, u8 reg) |
@@ -1621,7 +1603,7 @@ static int adv7604_get_edid(struct v4l2_subdev *sd, struct v4l2_subdev_edid *edi | |||
1621 | return 0; | 1603 | return 0; |
1622 | } | 1604 | } |
1623 | 1605 | ||
1624 | static int get_edid_spa_location(struct v4l2_subdev *sd, const u8 *edid) | 1606 | static int get_edid_spa_location(const u8 *edid) |
1625 | { | 1607 | { |
1626 | u8 d; | 1608 | u8 d; |
1627 | 1609 | ||
@@ -1652,9 +1634,10 @@ static int get_edid_spa_location(struct v4l2_subdev *sd, const u8 *edid) | |||
1652 | static int adv7604_set_edid(struct v4l2_subdev *sd, struct v4l2_subdev_edid *edid) | 1634 | static int adv7604_set_edid(struct v4l2_subdev *sd, struct v4l2_subdev_edid *edid) |
1653 | { | 1635 | { |
1654 | struct adv7604_state *state = to_state(sd); | 1636 | struct adv7604_state *state = to_state(sd); |
1655 | int spa_loc = get_edid_spa_location(sd, edid->edid); | 1637 | int spa_loc; |
1656 | int tmp = 0; | 1638 | int tmp = 0; |
1657 | int err; | 1639 | int err; |
1640 | int i; | ||
1658 | 1641 | ||
1659 | if (edid->pad > ADV7604_EDID_PORT_D) | 1642 | if (edid->pad > ADV7604_EDID_PORT_D) |
1660 | return -EINVAL; | 1643 | return -EINVAL; |
@@ -1684,35 +1667,43 @@ static int adv7604_set_edid(struct v4l2_subdev *sd, struct v4l2_subdev_edid *edi | |||
1684 | if (!edid->edid) | 1667 | if (!edid->edid) |
1685 | return -EINVAL; | 1668 | return -EINVAL; |
1686 | 1669 | ||
1670 | v4l2_dbg(2, debug, sd, "%s: write EDID pad %d, edid.present = 0x%x\n", | ||
1671 | __func__, edid->pad, state->edid.present); | ||
1672 | |||
1687 | /* Disable hotplug and I2C access to EDID RAM from DDC port */ | 1673 | /* Disable hotplug and I2C access to EDID RAM from DDC port */ |
1688 | cancel_delayed_work_sync(&state->delayed_work_enable_hotplug); | 1674 | cancel_delayed_work_sync(&state->delayed_work_enable_hotplug); |
1689 | v4l2_subdev_notify(sd, ADV7604_HOTPLUG, (void *)&tmp); | 1675 | v4l2_subdev_notify(sd, ADV7604_HOTPLUG, (void *)&tmp); |
1690 | rep_write_and_or(sd, 0x77, 0xf0, 0x00); | 1676 | rep_write_and_or(sd, 0x77, 0xf0, 0x00); |
1691 | 1677 | ||
1692 | v4l2_dbg(2, debug, sd, "%s: write EDID pad %d, edid.present = 0x%x\n", | 1678 | spa_loc = get_edid_spa_location(edid->edid); |
1693 | __func__, edid->pad, state->edid.present); | 1679 | if (spa_loc < 0) |
1680 | spa_loc = 0xc0; /* Default value [REF_02, p. 116] */ | ||
1681 | |||
1694 | switch (edid->pad) { | 1682 | switch (edid->pad) { |
1695 | case ADV7604_EDID_PORT_A: | 1683 | case ADV7604_EDID_PORT_A: |
1696 | state->spa_port_a = edid->edid[spa_loc]; | 1684 | state->spa_port_a[0] = edid->edid[spa_loc]; |
1685 | state->spa_port_a[1] = edid->edid[spa_loc + 1]; | ||
1697 | break; | 1686 | break; |
1698 | case ADV7604_EDID_PORT_B: | 1687 | case ADV7604_EDID_PORT_B: |
1699 | rep_write(sd, 0x70, (spa_loc < 0) ? 0 : edid->edid[spa_loc]); | 1688 | rep_write(sd, 0x70, edid->edid[spa_loc]); |
1700 | rep_write(sd, 0x71, (spa_loc < 0) ? 0 : edid->edid[spa_loc + 1]); | 1689 | rep_write(sd, 0x71, edid->edid[spa_loc + 1]); |
1701 | break; | 1690 | break; |
1702 | case ADV7604_EDID_PORT_C: | 1691 | case ADV7604_EDID_PORT_C: |
1703 | rep_write(sd, 0x72, (spa_loc < 0) ? 0 : edid->edid[spa_loc]); | 1692 | rep_write(sd, 0x72, edid->edid[spa_loc]); |
1704 | rep_write(sd, 0x73, (spa_loc < 0) ? 0 : edid->edid[spa_loc + 1]); | 1693 | rep_write(sd, 0x73, edid->edid[spa_loc + 1]); |
1705 | break; | 1694 | break; |
1706 | case ADV7604_EDID_PORT_D: | 1695 | case ADV7604_EDID_PORT_D: |
1707 | rep_write(sd, 0x74, (spa_loc < 0) ? 0 : edid->edid[spa_loc]); | 1696 | rep_write(sd, 0x74, edid->edid[spa_loc]); |
1708 | rep_write(sd, 0x75, (spa_loc < 0) ? 0 : edid->edid[spa_loc + 1]); | 1697 | rep_write(sd, 0x75, edid->edid[spa_loc + 1]); |
1709 | break; | 1698 | break; |
1699 | default: | ||
1700 | return -EINVAL; | ||
1710 | } | 1701 | } |
1711 | rep_write(sd, 0x76, (spa_loc < 0) ? 0x00 : spa_loc); | 1702 | rep_write(sd, 0x76, spa_loc & 0xff); |
1712 | rep_write_and_or(sd, 0x77, 0xbf, (spa_loc < 0) ? 0x00 : (spa_loc >> 2) & 0x40); | 1703 | rep_write_and_or(sd, 0x77, 0xbf, (spa_loc >> 2) & 0x40); |
1713 | 1704 | ||
1714 | if (spa_loc > 0) | 1705 | edid->edid[spa_loc] = state->spa_port_a[0]; |
1715 | edid->edid[spa_loc] = state->spa_port_a; | 1706 | edid->edid[spa_loc + 1] = state->spa_port_a[1]; |
1716 | 1707 | ||
1717 | memcpy(state->edid.edid, edid->edid, 128 * edid->blocks); | 1708 | memcpy(state->edid.edid, edid->edid, 128 * edid->blocks); |
1718 | state->edid.blocks = edid->blocks; | 1709 | state->edid.blocks = edid->blocks; |
@@ -1726,6 +1717,21 @@ static int adv7604_set_edid(struct v4l2_subdev *sd, struct v4l2_subdev_edid *edi | |||
1726 | return err; | 1717 | return err; |
1727 | } | 1718 | } |
1728 | 1719 | ||
1720 | /* adv7604 calculates the checksums and enables I2C access to internal | ||
1721 | EDID RAM from DDC port. */ | ||
1722 | rep_write_and_or(sd, 0x77, 0xf0, state->edid.present); | ||
1723 | |||
1724 | for (i = 0; i < 1000; i++) { | ||
1725 | if (rep_read(sd, 0x7d) & state->edid.present) | ||
1726 | break; | ||
1727 | mdelay(1); | ||
1728 | } | ||
1729 | if (i == 1000) { | ||
1730 | v4l2_err(sd, "error enabling edid (0x%x)\n", state->edid.present); | ||
1731 | return -EIO; | ||
1732 | } | ||
1733 | |||
1734 | |||
1729 | /* enable hotplug after 100 ms */ | 1735 | /* enable hotplug after 100 ms */ |
1730 | queue_delayed_work(state->work_queues, | 1736 | queue_delayed_work(state->work_queues, |
1731 | &state->delayed_work_enable_hotplug, HZ / 10); | 1737 | &state->delayed_work_enable_hotplug, HZ / 10); |