.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
---|
1 | 2 | /* |
---|
2 | 3 | * A V4L2 driver for OmniVision OV7670 cameras. |
---|
3 | 4 | * |
---|
.. | .. |
---|
6 | 7 | * McClelland's ovcamchip code. |
---|
7 | 8 | * |
---|
8 | 9 | * Copyright 2006-7 Jonathan Corbet <corbet@lwn.net> |
---|
9 | | - * |
---|
10 | | - * This file may be distributed under the terms of the GNU General |
---|
11 | | - * Public License, version 2. |
---|
12 | 10 | */ |
---|
13 | 11 | #include <linux/clk.h> |
---|
14 | 12 | #include <linux/init.h> |
---|
.. | .. |
---|
20 | 18 | #include <linux/gpio.h> |
---|
21 | 19 | #include <linux/gpio/consumer.h> |
---|
22 | 20 | #include <media/v4l2-device.h> |
---|
| 21 | +#include <media/v4l2-event.h> |
---|
23 | 22 | #include <media/v4l2-ctrls.h> |
---|
24 | 23 | #include <media/v4l2-fwnode.h> |
---|
25 | 24 | #include <media/v4l2-mediabus.h> |
---|
.. | .. |
---|
240 | 239 | }; |
---|
241 | 240 | struct v4l2_mbus_framefmt format; |
---|
242 | 241 | struct ov7670_format_struct *fmt; /* Current format */ |
---|
| 242 | + struct ov7670_win_size *wsize; |
---|
243 | 243 | struct clk *clk; |
---|
| 244 | + int on; |
---|
244 | 245 | struct gpio_desc *resetb_gpio; |
---|
245 | 246 | struct gpio_desc *pwdn_gpio; |
---|
246 | 247 | unsigned int mbus_config; /* Media bus configuration flags */ |
---|
.. | .. |
---|
809 | 810 | (4 * clkrc); |
---|
810 | 811 | } |
---|
811 | 812 | |
---|
| 813 | +static int ov7675_apply_framerate(struct v4l2_subdev *sd) |
---|
| 814 | +{ |
---|
| 815 | + struct ov7670_info *info = to_state(sd); |
---|
| 816 | + int ret; |
---|
| 817 | + |
---|
| 818 | + ret = ov7670_write(sd, REG_CLKRC, info->clkrc); |
---|
| 819 | + if (ret < 0) |
---|
| 820 | + return ret; |
---|
| 821 | + |
---|
| 822 | + return ov7670_write(sd, REG_DBLV, |
---|
| 823 | + info->pll_bypass ? DBLV_BYPASS : DBLV_X4); |
---|
| 824 | +} |
---|
| 825 | + |
---|
812 | 826 | static int ov7675_set_framerate(struct v4l2_subdev *sd, |
---|
813 | 827 | struct v4l2_fract *tpf) |
---|
814 | 828 | { |
---|
815 | 829 | struct ov7670_info *info = to_state(sd); |
---|
816 | 830 | u32 clkrc; |
---|
817 | 831 | int pll_factor; |
---|
818 | | - int ret; |
---|
819 | 832 | |
---|
820 | 833 | /* |
---|
821 | 834 | * The formula is fps = 5/4*pixclk for YUV/RGB and |
---|
.. | .. |
---|
824 | 837 | * pixclk = clock_speed / (clkrc + 1) * PLLfactor |
---|
825 | 838 | * |
---|
826 | 839 | */ |
---|
827 | | - if (info->pll_bypass) { |
---|
828 | | - pll_factor = 1; |
---|
829 | | - ret = ov7670_write(sd, REG_DBLV, DBLV_BYPASS); |
---|
830 | | - } else { |
---|
831 | | - pll_factor = PLL_FACTOR; |
---|
832 | | - ret = ov7670_write(sd, REG_DBLV, DBLV_X4); |
---|
833 | | - } |
---|
834 | | - if (ret < 0) |
---|
835 | | - return ret; |
---|
836 | | - |
---|
837 | 840 | if (tpf->numerator == 0 || tpf->denominator == 0) { |
---|
838 | 841 | clkrc = 0; |
---|
839 | 842 | } else { |
---|
| 843 | + pll_factor = info->pll_bypass ? 1 : PLL_FACTOR; |
---|
840 | 844 | clkrc = (5 * pll_factor * info->clock_speed * tpf->numerator) / |
---|
841 | 845 | (4 * tpf->denominator); |
---|
842 | 846 | if (info->fmt->mbus_code == MEDIA_BUS_FMT_SBGGR8_1X8) |
---|
.. | .. |
---|
858 | 862 | /* Recalculate frame rate */ |
---|
859 | 863 | ov7675_get_framerate(sd, tpf); |
---|
860 | 864 | |
---|
861 | | - ret = ov7670_write(sd, REG_CLKRC, info->clkrc); |
---|
862 | | - if (ret < 0) |
---|
863 | | - return ret; |
---|
| 865 | + /* |
---|
| 866 | + * If the device is not powered up by the host driver do |
---|
| 867 | + * not apply any changes to H/W at this time. Instead |
---|
| 868 | + * the framerate will be restored right after power-up. |
---|
| 869 | + */ |
---|
| 870 | + if (info->on) |
---|
| 871 | + return ov7675_apply_framerate(sd); |
---|
864 | 872 | |
---|
865 | 873 | return 0; |
---|
866 | 874 | } |
---|
.. | .. |
---|
893 | 901 | info->clkrc = (info->clkrc & 0x80) | div; |
---|
894 | 902 | tpf->numerator = 1; |
---|
895 | 903 | tpf->denominator = info->clock_speed / div; |
---|
896 | | - return ov7670_write(sd, REG_CLKRC, info->clkrc); |
---|
| 904 | + |
---|
| 905 | + /* |
---|
| 906 | + * If the device is not powered up by the host driver do |
---|
| 907 | + * not apply any changes to H/W at this time. Instead |
---|
| 908 | + * the framerate will be restored right after power-up. |
---|
| 909 | + */ |
---|
| 910 | + if (info->on) |
---|
| 911 | + return ov7670_write(sd, REG_CLKRC, info->clkrc); |
---|
| 912 | + |
---|
| 913 | + return 0; |
---|
897 | 914 | } |
---|
898 | 915 | |
---|
899 | 916 | /* |
---|
.. | .. |
---|
1003 | 1020 | return 0; |
---|
1004 | 1021 | } |
---|
1005 | 1022 | |
---|
1006 | | -/* |
---|
1007 | | - * Set a format. |
---|
1008 | | - */ |
---|
1009 | | -static int ov7670_set_fmt(struct v4l2_subdev *sd, |
---|
1010 | | - struct v4l2_subdev_pad_config *cfg, |
---|
1011 | | - struct v4l2_subdev_format *format) |
---|
| 1023 | +static int ov7670_apply_fmt(struct v4l2_subdev *sd) |
---|
1012 | 1024 | { |
---|
1013 | | - struct ov7670_format_struct *ovfmt; |
---|
1014 | | - struct ov7670_win_size *wsize; |
---|
1015 | 1025 | struct ov7670_info *info = to_state(sd); |
---|
1016 | | -#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API |
---|
1017 | | - struct v4l2_mbus_framefmt *mbus_fmt; |
---|
1018 | | -#endif |
---|
| 1026 | + struct ov7670_win_size *wsize = info->wsize; |
---|
1019 | 1027 | unsigned char com7, com10 = 0; |
---|
1020 | 1028 | int ret; |
---|
1021 | 1029 | |
---|
1022 | | - if (format->pad) |
---|
1023 | | - return -EINVAL; |
---|
1024 | | - |
---|
1025 | | - if (format->which == V4L2_SUBDEV_FORMAT_TRY) { |
---|
1026 | | - ret = ov7670_try_fmt_internal(sd, &format->format, NULL, NULL); |
---|
1027 | | - if (ret) |
---|
1028 | | - return ret; |
---|
1029 | | -#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API |
---|
1030 | | - mbus_fmt = v4l2_subdev_get_try_format(sd, cfg, format->pad); |
---|
1031 | | - *mbus_fmt = format->format; |
---|
1032 | | - return 0; |
---|
1033 | | -#else |
---|
1034 | | - return -ENOTTY; |
---|
1035 | | -#endif |
---|
1036 | | - } |
---|
1037 | | - |
---|
1038 | | - ret = ov7670_try_fmt_internal(sd, &format->format, &ovfmt, &wsize); |
---|
1039 | | - if (ret) |
---|
1040 | | - return ret; |
---|
1041 | 1030 | /* |
---|
1042 | 1031 | * COM7 is a pain in the ass, it doesn't like to be read then |
---|
1043 | 1032 | * quickly written afterward. But we have everything we need |
---|
1044 | 1033 | * to set it absolutely here, as long as the format-specific |
---|
1045 | 1034 | * register sets list it first. |
---|
1046 | 1035 | */ |
---|
1047 | | - com7 = ovfmt->regs[0].value; |
---|
| 1036 | + com7 = info->fmt->regs[0].value; |
---|
1048 | 1037 | com7 |= wsize->com7_bit; |
---|
1049 | 1038 | ret = ov7670_write(sd, REG_COM7, com7); |
---|
1050 | 1039 | if (ret) |
---|
.. | .. |
---|
1066 | 1055 | /* |
---|
1067 | 1056 | * Now write the rest of the array. Also store start/stops |
---|
1068 | 1057 | */ |
---|
1069 | | - ret = ov7670_write_array(sd, ovfmt->regs + 1); |
---|
| 1058 | + ret = ov7670_write_array(sd, info->fmt->regs + 1); |
---|
1070 | 1059 | if (ret) |
---|
1071 | 1060 | return ret; |
---|
1072 | 1061 | |
---|
.. | .. |
---|
1081 | 1070 | return ret; |
---|
1082 | 1071 | } |
---|
1083 | 1072 | |
---|
1084 | | - info->fmt = ovfmt; |
---|
1085 | | - |
---|
1086 | 1073 | /* |
---|
1087 | 1074 | * If we're running RGB565, we must rewrite clkrc after setting |
---|
1088 | 1075 | * the other parameters or the image looks poor. If we're *not* |
---|
.. | .. |
---|
1096 | 1083 | ret = ov7670_write(sd, REG_CLKRC, info->clkrc); |
---|
1097 | 1084 | if (ret) |
---|
1098 | 1085 | return ret; |
---|
| 1086 | + |
---|
| 1087 | + return 0; |
---|
| 1088 | +} |
---|
| 1089 | + |
---|
| 1090 | +/* |
---|
| 1091 | + * Set a format. |
---|
| 1092 | + */ |
---|
| 1093 | +static int ov7670_set_fmt(struct v4l2_subdev *sd, |
---|
| 1094 | + struct v4l2_subdev_pad_config *cfg, |
---|
| 1095 | + struct v4l2_subdev_format *format) |
---|
| 1096 | +{ |
---|
| 1097 | + struct ov7670_info *info = to_state(sd); |
---|
| 1098 | +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API |
---|
| 1099 | + struct v4l2_mbus_framefmt *mbus_fmt; |
---|
| 1100 | +#endif |
---|
| 1101 | + int ret; |
---|
| 1102 | + |
---|
| 1103 | + if (format->pad) |
---|
| 1104 | + return -EINVAL; |
---|
| 1105 | + |
---|
| 1106 | + if (format->which == V4L2_SUBDEV_FORMAT_TRY) { |
---|
| 1107 | + ret = ov7670_try_fmt_internal(sd, &format->format, NULL, NULL); |
---|
| 1108 | + if (ret) |
---|
| 1109 | + return ret; |
---|
| 1110 | +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API |
---|
| 1111 | + mbus_fmt = v4l2_subdev_get_try_format(sd, cfg, format->pad); |
---|
| 1112 | + *mbus_fmt = format->format; |
---|
| 1113 | +#endif |
---|
| 1114 | + return 0; |
---|
| 1115 | + } |
---|
| 1116 | + |
---|
| 1117 | + ret = ov7670_try_fmt_internal(sd, &format->format, &info->fmt, &info->wsize); |
---|
| 1118 | + if (ret) |
---|
| 1119 | + return ret; |
---|
| 1120 | + |
---|
| 1121 | + /* |
---|
| 1122 | + * If the device is not powered up by the host driver do |
---|
| 1123 | + * not apply any changes to H/W at this time. Instead |
---|
| 1124 | + * the frame format will be restored right after power-up. |
---|
| 1125 | + */ |
---|
| 1126 | + if (info->on) |
---|
| 1127 | + return ov7670_apply_fmt(sd); |
---|
1099 | 1128 | |
---|
1100 | 1129 | return 0; |
---|
1101 | 1130 | } |
---|
.. | .. |
---|
1115 | 1144 | format->format = *mbus_fmt; |
---|
1116 | 1145 | return 0; |
---|
1117 | 1146 | #else |
---|
1118 | | - return -ENOTTY; |
---|
| 1147 | + return -EINVAL; |
---|
1119 | 1148 | #endif |
---|
1120 | 1149 | } else { |
---|
1121 | 1150 | format->format = info->format; |
---|
.. | .. |
---|
1606 | 1635 | } |
---|
1607 | 1636 | #endif |
---|
1608 | 1637 | |
---|
| 1638 | +static void ov7670_power_on(struct v4l2_subdev *sd) |
---|
| 1639 | +{ |
---|
| 1640 | + struct ov7670_info *info = to_state(sd); |
---|
| 1641 | + |
---|
| 1642 | + if (info->on) |
---|
| 1643 | + return; |
---|
| 1644 | + |
---|
| 1645 | + clk_prepare_enable(info->clk); |
---|
| 1646 | + |
---|
| 1647 | + if (info->pwdn_gpio) |
---|
| 1648 | + gpiod_set_value(info->pwdn_gpio, 0); |
---|
| 1649 | + if (info->resetb_gpio) { |
---|
| 1650 | + gpiod_set_value(info->resetb_gpio, 1); |
---|
| 1651 | + usleep_range(500, 1000); |
---|
| 1652 | + gpiod_set_value(info->resetb_gpio, 0); |
---|
| 1653 | + } |
---|
| 1654 | + if (info->pwdn_gpio || info->resetb_gpio || info->clk) |
---|
| 1655 | + usleep_range(3000, 5000); |
---|
| 1656 | + |
---|
| 1657 | + info->on = true; |
---|
| 1658 | +} |
---|
| 1659 | + |
---|
| 1660 | +static void ov7670_power_off(struct v4l2_subdev *sd) |
---|
| 1661 | +{ |
---|
| 1662 | + struct ov7670_info *info = to_state(sd); |
---|
| 1663 | + |
---|
| 1664 | + if (!info->on) |
---|
| 1665 | + return; |
---|
| 1666 | + |
---|
| 1667 | + clk_disable_unprepare(info->clk); |
---|
| 1668 | + |
---|
| 1669 | + if (info->pwdn_gpio) |
---|
| 1670 | + gpiod_set_value(info->pwdn_gpio, 1); |
---|
| 1671 | + |
---|
| 1672 | + info->on = false; |
---|
| 1673 | +} |
---|
| 1674 | + |
---|
1609 | 1675 | static int ov7670_s_power(struct v4l2_subdev *sd, int on) |
---|
1610 | 1676 | { |
---|
1611 | 1677 | struct ov7670_info *info = to_state(sd); |
---|
1612 | 1678 | |
---|
1613 | | - if (info->pwdn_gpio) |
---|
1614 | | - gpiod_set_value(info->pwdn_gpio, !on); |
---|
1615 | | - if (on && info->resetb_gpio) { |
---|
1616 | | - gpiod_set_value(info->resetb_gpio, 1); |
---|
1617 | | - usleep_range(500, 1000); |
---|
1618 | | - gpiod_set_value(info->resetb_gpio, 0); |
---|
1619 | | - usleep_range(3000, 5000); |
---|
| 1679 | + if (info->on == on) |
---|
| 1680 | + return 0; |
---|
| 1681 | + |
---|
| 1682 | + if (on) { |
---|
| 1683 | + ov7670_power_on (sd); |
---|
| 1684 | + ov7670_init(sd, 0); |
---|
| 1685 | + ov7670_apply_fmt(sd); |
---|
| 1686 | + ov7675_apply_framerate(sd); |
---|
| 1687 | + v4l2_ctrl_handler_setup(&info->hdl); |
---|
| 1688 | + } else { |
---|
| 1689 | + ov7670_power_off (sd); |
---|
1620 | 1690 | } |
---|
1621 | 1691 | |
---|
1622 | 1692 | return 0; |
---|
.. | .. |
---|
1651 | 1721 | static const struct v4l2_subdev_core_ops ov7670_core_ops = { |
---|
1652 | 1722 | .reset = ov7670_reset, |
---|
1653 | 1723 | .init = ov7670_init, |
---|
| 1724 | + .s_power = ov7670_s_power, |
---|
| 1725 | + .log_status = v4l2_ctrl_subdev_log_status, |
---|
| 1726 | + .subscribe_event = v4l2_ctrl_subdev_subscribe_event, |
---|
| 1727 | + .unsubscribe_event = v4l2_event_subdev_unsubscribe, |
---|
1654 | 1728 | #ifdef CONFIG_VIDEO_ADV_DEBUG |
---|
1655 | 1729 | .g_register = ov7670_g_register, |
---|
1656 | 1730 | .s_register = ov7670_s_register, |
---|
.. | .. |
---|
1728 | 1802 | struct ov7670_info *info) |
---|
1729 | 1803 | { |
---|
1730 | 1804 | struct fwnode_handle *fwnode = dev_fwnode(dev); |
---|
1731 | | - struct v4l2_fwnode_endpoint bus_cfg; |
---|
| 1805 | + struct v4l2_fwnode_endpoint bus_cfg = { .bus_type = 0 }; |
---|
1732 | 1806 | struct fwnode_handle *ep; |
---|
1733 | 1807 | int ret; |
---|
1734 | 1808 | |
---|
.. | .. |
---|
1773 | 1847 | |
---|
1774 | 1848 | #ifdef CONFIG_VIDEO_V4L2_SUBDEV_API |
---|
1775 | 1849 | sd->internal_ops = &ov7670_subdev_internal_ops; |
---|
1776 | | - sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; |
---|
| 1850 | + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS; |
---|
1777 | 1851 | #endif |
---|
1778 | 1852 | |
---|
1779 | 1853 | info->clock_speed = 30; /* default: a guess */ |
---|
.. | .. |
---|
1812 | 1886 | else |
---|
1813 | 1887 | return ret; |
---|
1814 | 1888 | } |
---|
1815 | | - if (info->clk) { |
---|
1816 | | - ret = clk_prepare_enable(info->clk); |
---|
1817 | | - if (ret) |
---|
1818 | | - return ret; |
---|
1819 | | - |
---|
1820 | | - info->clock_speed = clk_get_rate(info->clk) / 1000000; |
---|
1821 | | - if (info->clock_speed < 10 || info->clock_speed > 48) { |
---|
1822 | | - ret = -EINVAL; |
---|
1823 | | - goto clk_disable; |
---|
1824 | | - } |
---|
1825 | | - } |
---|
1826 | 1889 | |
---|
1827 | 1890 | ret = ov7670_init_gpio(client, info); |
---|
1828 | 1891 | if (ret) |
---|
1829 | | - goto clk_disable; |
---|
| 1892 | + return ret; |
---|
1830 | 1893 | |
---|
1831 | | - ov7670_s_power(sd, 1); |
---|
| 1894 | + ov7670_power_on(sd); |
---|
| 1895 | + |
---|
| 1896 | + if (info->clk) { |
---|
| 1897 | + info->clock_speed = clk_get_rate(info->clk) / 1000000; |
---|
| 1898 | + if (info->clock_speed < 10 || info->clock_speed > 48) { |
---|
| 1899 | + ret = -EINVAL; |
---|
| 1900 | + goto power_off; |
---|
| 1901 | + } |
---|
| 1902 | + } |
---|
1832 | 1903 | |
---|
1833 | 1904 | /* Make sure it's an ov7670 */ |
---|
1834 | 1905 | ret = ov7670_detect(sd); |
---|
.. | .. |
---|
1843 | 1914 | |
---|
1844 | 1915 | info->devtype = &ov7670_devdata[id->driver_data]; |
---|
1845 | 1916 | info->fmt = &ov7670_formats[0]; |
---|
| 1917 | + info->wsize = &info->devtype->win_sizes[0]; |
---|
1846 | 1918 | |
---|
1847 | 1919 | ov7670_get_default_format(sd, &info->format); |
---|
1848 | 1920 | |
---|
.. | .. |
---|
1908 | 1980 | if (ret < 0) |
---|
1909 | 1981 | goto entity_cleanup; |
---|
1910 | 1982 | |
---|
| 1983 | + ov7670_power_off(sd); |
---|
1911 | 1984 | return 0; |
---|
1912 | 1985 | |
---|
1913 | 1986 | entity_cleanup: |
---|
.. | .. |
---|
1915 | 1988 | hdl_free: |
---|
1916 | 1989 | v4l2_ctrl_handler_free(&info->hdl); |
---|
1917 | 1990 | power_off: |
---|
1918 | | - ov7670_s_power(sd, 0); |
---|
1919 | | -clk_disable: |
---|
1920 | | - clk_disable_unprepare(info->clk); |
---|
| 1991 | + ov7670_power_off(sd); |
---|
1921 | 1992 | return ret; |
---|
1922 | 1993 | } |
---|
1923 | | - |
---|
1924 | 1994 | |
---|
1925 | 1995 | static int ov7670_remove(struct i2c_client *client) |
---|
1926 | 1996 | { |
---|
.. | .. |
---|
1929 | 1999 | |
---|
1930 | 2000 | v4l2_async_unregister_subdev(sd); |
---|
1931 | 2001 | v4l2_ctrl_handler_free(&info->hdl); |
---|
1932 | | - clk_disable_unprepare(info->clk); |
---|
1933 | 2002 | media_entity_cleanup(&info->sd.entity); |
---|
1934 | | - ov7670_s_power(sd, 0); |
---|
1935 | 2003 | return 0; |
---|
1936 | 2004 | } |
---|
1937 | 2005 | |
---|