16a26f141SJacopo Mondi // SPDX-License-Identifier: GPL-2.0 27641b044SJacopo Mondi /* 37641b044SJacopo Mondi * mt9t112 Camera Driver 47641b044SJacopo Mondi * 56a26f141SJacopo Mondi * Copyright (C) 2018 Jacopo Mondi <jacopo+renesas@jmondi.org> 66a26f141SJacopo Mondi * 77641b044SJacopo Mondi * Copyright (C) 2009 Renesas Solutions Corp. 87641b044SJacopo Mondi * Kuninori Morimoto <morimoto.kuninori@renesas.com> 97641b044SJacopo Mondi * 107641b044SJacopo Mondi * Based on ov772x driver, mt9m111 driver, 117641b044SJacopo Mondi * 127641b044SJacopo Mondi * Copyright (C) 2008 Kuninori Morimoto <morimoto.kuninori@renesas.com> 137641b044SJacopo Mondi * Copyright (C) 2008, Robert Jarzmik <robert.jarzmik@free.fr> 147641b044SJacopo Mondi * Copyright 2006-7 Jonathan Corbet <corbet@lwn.net> 157641b044SJacopo Mondi * Copyright (C) 2008 Magnus Damm 167641b044SJacopo Mondi * Copyright (C) 2008, Guennadi Liakhovetski <kernel@pengutronix.de> 177641b044SJacopo Mondi * 186a26f141SJacopo Mondi * TODO: This driver lacks support for frame rate control due to missing 196a26f141SJacopo Mondi * register level documentation and suitable hardware for testing. 206a26f141SJacopo Mondi * v4l-utils compliance tools will report errors. 217641b044SJacopo Mondi */ 227641b044SJacopo Mondi 236a26f141SJacopo Mondi #include <linux/clk.h> 247641b044SJacopo Mondi #include <linux/delay.h> 256a26f141SJacopo Mondi #include <linux/gpio/consumer.h> 267641b044SJacopo Mondi #include <linux/i2c.h> 277641b044SJacopo Mondi #include <linux/init.h> 287641b044SJacopo Mondi #include <linux/module.h> 297641b044SJacopo Mondi #include <linux/slab.h> 307641b044SJacopo Mondi #include <linux/v4l2-mediabus.h> 317641b044SJacopo Mondi #include <linux/videodev2.h> 327641b044SJacopo Mondi 337641b044SJacopo Mondi #include <media/i2c/mt9t112.h> 347641b044SJacopo Mondi #include <media/v4l2-common.h> 357641b044SJacopo Mondi #include <media/v4l2-image-sizes.h> 366a26f141SJacopo Mondi #include <media/v4l2-subdev.h> 377641b044SJacopo Mondi 387641b044SJacopo Mondi /* you can check PLL/clock info */ 397641b044SJacopo Mondi /* #define EXT_CLOCK 24000000 */ 407641b044SJacopo Mondi 417641b044SJacopo Mondi /************************************************************************ 426a26f141SJacopo Mondi * macro 436a26f141SJacopo Mondi ***********************************************************************/ 447641b044SJacopo Mondi /* 457641b044SJacopo Mondi * frame size 467641b044SJacopo Mondi */ 477641b044SJacopo Mondi #define MAX_WIDTH 2048 487641b044SJacopo Mondi #define MAX_HEIGHT 1536 497641b044SJacopo Mondi 507641b044SJacopo Mondi /* 517641b044SJacopo Mondi * macro of read/write 527641b044SJacopo Mondi */ 537641b044SJacopo Mondi #define ECHECKER(ret, x) \ 547641b044SJacopo Mondi do { \ 557641b044SJacopo Mondi (ret) = (x); \ 567641b044SJacopo Mondi if ((ret) < 0) \ 577641b044SJacopo Mondi return (ret); \ 587641b044SJacopo Mondi } while (0) 597641b044SJacopo Mondi 607641b044SJacopo Mondi #define mt9t112_reg_write(ret, client, a, b) \ 617641b044SJacopo Mondi ECHECKER(ret, __mt9t112_reg_write(client, a, b)) 627641b044SJacopo Mondi #define mt9t112_mcu_write(ret, client, a, b) \ 637641b044SJacopo Mondi ECHECKER(ret, __mt9t112_mcu_write(client, a, b)) 647641b044SJacopo Mondi 657641b044SJacopo Mondi #define mt9t112_reg_mask_set(ret, client, a, b, c) \ 667641b044SJacopo Mondi ECHECKER(ret, __mt9t112_reg_mask_set(client, a, b, c)) 677641b044SJacopo Mondi #define mt9t112_mcu_mask_set(ret, client, a, b, c) \ 687641b044SJacopo Mondi ECHECKER(ret, __mt9t112_mcu_mask_set(client, a, b, c)) 697641b044SJacopo Mondi 707641b044SJacopo Mondi #define mt9t112_reg_read(ret, client, a) \ 717641b044SJacopo Mondi ECHECKER(ret, __mt9t112_reg_read(client, a)) 727641b044SJacopo Mondi 737641b044SJacopo Mondi /* 747641b044SJacopo Mondi * Logical address 757641b044SJacopo Mondi */ 767641b044SJacopo Mondi #define _VAR(id, offset, base) (base | (id & 0x1f) << 10 | (offset & 0x3ff)) 777641b044SJacopo Mondi #define VAR(id, offset) _VAR(id, offset, 0x0000) 787641b044SJacopo Mondi #define VAR8(id, offset) _VAR(id, offset, 0x8000) 797641b044SJacopo Mondi 807641b044SJacopo Mondi /************************************************************************ 816a26f141SJacopo Mondi * struct 826a26f141SJacopo Mondi ***********************************************************************/ 837641b044SJacopo Mondi struct mt9t112_format { 847641b044SJacopo Mondi u32 code; 857641b044SJacopo Mondi enum v4l2_colorspace colorspace; 867641b044SJacopo Mondi u16 fmt; 877641b044SJacopo Mondi u16 order; 887641b044SJacopo Mondi }; 897641b044SJacopo Mondi 907641b044SJacopo Mondi struct mt9t112_priv { 917641b044SJacopo Mondi struct v4l2_subdev subdev; 926a26f141SJacopo Mondi struct mt9t112_platform_data *info; 937641b044SJacopo Mondi struct i2c_client *client; 947641b044SJacopo Mondi struct v4l2_rect frame; 956a26f141SJacopo Mondi struct clk *clk; 966a26f141SJacopo Mondi struct gpio_desc *standby_gpio; 977641b044SJacopo Mondi const struct mt9t112_format *format; 987641b044SJacopo Mondi int num_formats; 996a26f141SJacopo Mondi bool init_done; 1007641b044SJacopo Mondi }; 1017641b044SJacopo Mondi 1027641b044SJacopo Mondi /************************************************************************ 1036a26f141SJacopo Mondi * supported format 1046a26f141SJacopo Mondi ***********************************************************************/ 1057641b044SJacopo Mondi 1067641b044SJacopo Mondi static const struct mt9t112_format mt9t112_cfmts[] = { 1077641b044SJacopo Mondi { 1087641b044SJacopo Mondi .code = MEDIA_BUS_FMT_UYVY8_2X8, 1097641b044SJacopo Mondi .colorspace = V4L2_COLORSPACE_SRGB, 1107641b044SJacopo Mondi .fmt = 1, 1117641b044SJacopo Mondi .order = 0, 1127641b044SJacopo Mondi }, { 1137641b044SJacopo Mondi .code = MEDIA_BUS_FMT_VYUY8_2X8, 1147641b044SJacopo Mondi .colorspace = V4L2_COLORSPACE_SRGB, 1157641b044SJacopo Mondi .fmt = 1, 1167641b044SJacopo Mondi .order = 1, 1177641b044SJacopo Mondi }, { 1187641b044SJacopo Mondi .code = MEDIA_BUS_FMT_YUYV8_2X8, 1197641b044SJacopo Mondi .colorspace = V4L2_COLORSPACE_SRGB, 1207641b044SJacopo Mondi .fmt = 1, 1217641b044SJacopo Mondi .order = 2, 1227641b044SJacopo Mondi }, { 1237641b044SJacopo Mondi .code = MEDIA_BUS_FMT_YVYU8_2X8, 1247641b044SJacopo Mondi .colorspace = V4L2_COLORSPACE_SRGB, 1257641b044SJacopo Mondi .fmt = 1, 1267641b044SJacopo Mondi .order = 3, 1277641b044SJacopo Mondi }, { 1287641b044SJacopo Mondi .code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE, 1297641b044SJacopo Mondi .colorspace = V4L2_COLORSPACE_SRGB, 1307641b044SJacopo Mondi .fmt = 8, 1317641b044SJacopo Mondi .order = 2, 1327641b044SJacopo Mondi }, { 1337641b044SJacopo Mondi .code = MEDIA_BUS_FMT_RGB565_2X8_LE, 1347641b044SJacopo Mondi .colorspace = V4L2_COLORSPACE_SRGB, 1357641b044SJacopo Mondi .fmt = 4, 1367641b044SJacopo Mondi .order = 2, 1377641b044SJacopo Mondi }, 1387641b044SJacopo Mondi }; 1397641b044SJacopo Mondi 1407641b044SJacopo Mondi /************************************************************************ 1416a26f141SJacopo Mondi * general function 1426a26f141SJacopo Mondi ***********************************************************************/ 1437641b044SJacopo Mondi static struct mt9t112_priv *to_mt9t112(const struct i2c_client *client) 1447641b044SJacopo Mondi { 1457641b044SJacopo Mondi return container_of(i2c_get_clientdata(client), 1467641b044SJacopo Mondi struct mt9t112_priv, 1477641b044SJacopo Mondi subdev); 1487641b044SJacopo Mondi } 1497641b044SJacopo Mondi 1507641b044SJacopo Mondi static int __mt9t112_reg_read(const struct i2c_client *client, u16 command) 1517641b044SJacopo Mondi { 1527641b044SJacopo Mondi struct i2c_msg msg[2]; 1537641b044SJacopo Mondi u8 buf[2]; 1547641b044SJacopo Mondi int ret; 1557641b044SJacopo Mondi 1567641b044SJacopo Mondi command = swab16(command); 1577641b044SJacopo Mondi 1587641b044SJacopo Mondi msg[0].addr = client->addr; 1597641b044SJacopo Mondi msg[0].flags = 0; 1607641b044SJacopo Mondi msg[0].len = 2; 1617641b044SJacopo Mondi msg[0].buf = (u8 *)&command; 1627641b044SJacopo Mondi 1637641b044SJacopo Mondi msg[1].addr = client->addr; 1647641b044SJacopo Mondi msg[1].flags = I2C_M_RD; 1657641b044SJacopo Mondi msg[1].len = 2; 1667641b044SJacopo Mondi msg[1].buf = buf; 1677641b044SJacopo Mondi 1687641b044SJacopo Mondi /* 1696a26f141SJacopo Mondi * If return value of this function is < 0, it means error, else, 1706a26f141SJacopo Mondi * below 16bit is valid data. 1717641b044SJacopo Mondi */ 1727641b044SJacopo Mondi ret = i2c_transfer(client->adapter, msg, 2); 1737641b044SJacopo Mondi if (ret < 0) 1747641b044SJacopo Mondi return ret; 1757641b044SJacopo Mondi 1767641b044SJacopo Mondi memcpy(&ret, buf, 2); 1776a26f141SJacopo Mondi 1787641b044SJacopo Mondi return swab16(ret); 1797641b044SJacopo Mondi } 1807641b044SJacopo Mondi 1817641b044SJacopo Mondi static int __mt9t112_reg_write(const struct i2c_client *client, 1827641b044SJacopo Mondi u16 command, u16 data) 1837641b044SJacopo Mondi { 1847641b044SJacopo Mondi struct i2c_msg msg; 1857641b044SJacopo Mondi u8 buf[4]; 1867641b044SJacopo Mondi int ret; 1877641b044SJacopo Mondi 1887641b044SJacopo Mondi command = swab16(command); 1897641b044SJacopo Mondi data = swab16(data); 1907641b044SJacopo Mondi 1917641b044SJacopo Mondi memcpy(buf + 0, &command, 2); 1927641b044SJacopo Mondi memcpy(buf + 2, &data, 2); 1937641b044SJacopo Mondi 1947641b044SJacopo Mondi msg.addr = client->addr; 1957641b044SJacopo Mondi msg.flags = 0; 1967641b044SJacopo Mondi msg.len = 4; 1977641b044SJacopo Mondi msg.buf = buf; 1987641b044SJacopo Mondi 1997641b044SJacopo Mondi /* 2006a26f141SJacopo Mondi * i2c_transfer return message length, but this function should 2016a26f141SJacopo Mondi * return 0 if correct case. 2027641b044SJacopo Mondi */ 2037641b044SJacopo Mondi ret = i2c_transfer(client->adapter, &msg, 1); 2047641b044SJacopo Mondi 2056a26f141SJacopo Mondi return ret >= 0 ? 0 : ret; 2067641b044SJacopo Mondi } 2077641b044SJacopo Mondi 2087641b044SJacopo Mondi static int __mt9t112_reg_mask_set(const struct i2c_client *client, 2096a26f141SJacopo Mondi u16 command, u16 mask, u16 set) 2107641b044SJacopo Mondi { 2117641b044SJacopo Mondi int val = __mt9t112_reg_read(client, command); 2126a26f141SJacopo Mondi 2137641b044SJacopo Mondi if (val < 0) 2147641b044SJacopo Mondi return val; 2157641b044SJacopo Mondi 2167641b044SJacopo Mondi val &= ~mask; 2177641b044SJacopo Mondi val |= set & mask; 2187641b044SJacopo Mondi 2197641b044SJacopo Mondi return __mt9t112_reg_write(client, command, val); 2207641b044SJacopo Mondi } 2217641b044SJacopo Mondi 2227641b044SJacopo Mondi /* mcu access */ 2237641b044SJacopo Mondi static int __mt9t112_mcu_read(const struct i2c_client *client, u16 command) 2247641b044SJacopo Mondi { 2257641b044SJacopo Mondi int ret; 2267641b044SJacopo Mondi 2277641b044SJacopo Mondi ret = __mt9t112_reg_write(client, 0x098E, command); 2287641b044SJacopo Mondi if (ret < 0) 2297641b044SJacopo Mondi return ret; 2307641b044SJacopo Mondi 2317641b044SJacopo Mondi return __mt9t112_reg_read(client, 0x0990); 2327641b044SJacopo Mondi } 2337641b044SJacopo Mondi 2347641b044SJacopo Mondi static int __mt9t112_mcu_write(const struct i2c_client *client, 2357641b044SJacopo Mondi u16 command, u16 data) 2367641b044SJacopo Mondi { 2377641b044SJacopo Mondi int ret; 2387641b044SJacopo Mondi 2397641b044SJacopo Mondi ret = __mt9t112_reg_write(client, 0x098E, command); 2407641b044SJacopo Mondi if (ret < 0) 2417641b044SJacopo Mondi return ret; 2427641b044SJacopo Mondi 2437641b044SJacopo Mondi return __mt9t112_reg_write(client, 0x0990, data); 2447641b044SJacopo Mondi } 2457641b044SJacopo Mondi 2467641b044SJacopo Mondi static int __mt9t112_mcu_mask_set(const struct i2c_client *client, 2476a26f141SJacopo Mondi u16 command, u16 mask, u16 set) 2487641b044SJacopo Mondi { 2497641b044SJacopo Mondi int val = __mt9t112_mcu_read(client, command); 2506a26f141SJacopo Mondi 2517641b044SJacopo Mondi if (val < 0) 2527641b044SJacopo Mondi return val; 2537641b044SJacopo Mondi 2547641b044SJacopo Mondi val &= ~mask; 2557641b044SJacopo Mondi val |= set & mask; 2567641b044SJacopo Mondi 2577641b044SJacopo Mondi return __mt9t112_mcu_write(client, command, val); 2587641b044SJacopo Mondi } 2597641b044SJacopo Mondi 2607641b044SJacopo Mondi static int mt9t112_reset(const struct i2c_client *client) 2617641b044SJacopo Mondi { 2627641b044SJacopo Mondi int ret; 2637641b044SJacopo Mondi 2647641b044SJacopo Mondi mt9t112_reg_mask_set(ret, client, 0x001a, 0x0001, 0x0001); 2656a26f141SJacopo Mondi usleep_range(1000, 5000); 2667641b044SJacopo Mondi mt9t112_reg_mask_set(ret, client, 0x001a, 0x0001, 0x0000); 2677641b044SJacopo Mondi 2687641b044SJacopo Mondi return ret; 2697641b044SJacopo Mondi } 2707641b044SJacopo Mondi 2717641b044SJacopo Mondi #ifndef EXT_CLOCK 2727641b044SJacopo Mondi #define CLOCK_INFO(a, b) 2737641b044SJacopo Mondi #else 2747641b044SJacopo Mondi #define CLOCK_INFO(a, b) mt9t112_clock_info(a, b) 2757641b044SJacopo Mondi static int mt9t112_clock_info(const struct i2c_client *client, u32 ext) 2767641b044SJacopo Mondi { 2777641b044SJacopo Mondi int m, n, p1, p2, p3, p4, p5, p6, p7; 2787641b044SJacopo Mondi u32 vco, clk; 2797641b044SJacopo Mondi char *enable; 2807641b044SJacopo Mondi 2817641b044SJacopo Mondi ext /= 1000; /* kbyte order */ 2827641b044SJacopo Mondi 2837641b044SJacopo Mondi mt9t112_reg_read(n, client, 0x0012); 2847641b044SJacopo Mondi p1 = n & 0x000f; 2857641b044SJacopo Mondi n = n >> 4; 2867641b044SJacopo Mondi p2 = n & 0x000f; 2877641b044SJacopo Mondi n = n >> 4; 2887641b044SJacopo Mondi p3 = n & 0x000f; 2897641b044SJacopo Mondi 2907641b044SJacopo Mondi mt9t112_reg_read(n, client, 0x002a); 2917641b044SJacopo Mondi p4 = n & 0x000f; 2927641b044SJacopo Mondi n = n >> 4; 2937641b044SJacopo Mondi p5 = n & 0x000f; 2947641b044SJacopo Mondi n = n >> 4; 2957641b044SJacopo Mondi p6 = n & 0x000f; 2967641b044SJacopo Mondi 2977641b044SJacopo Mondi mt9t112_reg_read(n, client, 0x002c); 2987641b044SJacopo Mondi p7 = n & 0x000f; 2997641b044SJacopo Mondi 3007641b044SJacopo Mondi mt9t112_reg_read(n, client, 0x0010); 3017641b044SJacopo Mondi m = n & 0x00ff; 3027641b044SJacopo Mondi n = (n >> 8) & 0x003f; 3037641b044SJacopo Mondi 3046a26f141SJacopo Mondi enable = ((ext < 6000) || (ext > 54000)) ? "X" : ""; 3057641b044SJacopo Mondi dev_dbg(&client->dev, "EXTCLK : %10u K %s\n", ext, enable); 3067641b044SJacopo Mondi 3077641b044SJacopo Mondi vco = 2 * m * ext / (n + 1); 3086a26f141SJacopo Mondi enable = ((vco < 384000) || (vco > 768000)) ? "X" : ""; 3097641b044SJacopo Mondi dev_dbg(&client->dev, "VCO : %10u K %s\n", vco, enable); 3107641b044SJacopo Mondi 3117641b044SJacopo Mondi clk = vco / (p1 + 1) / (p2 + 1); 3126a26f141SJacopo Mondi enable = (clk > 96000) ? "X" : ""; 3137641b044SJacopo Mondi dev_dbg(&client->dev, "PIXCLK : %10u K %s\n", clk, enable); 3147641b044SJacopo Mondi 3157641b044SJacopo Mondi clk = vco / (p3 + 1); 3166a26f141SJacopo Mondi enable = (clk > 768000) ? "X" : ""; 3177641b044SJacopo Mondi dev_dbg(&client->dev, "MIPICLK : %10u K %s\n", clk, enable); 3187641b044SJacopo Mondi 3197641b044SJacopo Mondi clk = vco / (p6 + 1); 3206a26f141SJacopo Mondi enable = (clk > 96000) ? "X" : ""; 3217641b044SJacopo Mondi dev_dbg(&client->dev, "MCU CLK : %10u K %s\n", clk, enable); 3227641b044SJacopo Mondi 3237641b044SJacopo Mondi clk = vco / (p5 + 1); 3246a26f141SJacopo Mondi enable = (clk > 54000) ? "X" : ""; 3257641b044SJacopo Mondi dev_dbg(&client->dev, "SOC CLK : %10u K %s\n", clk, enable); 3267641b044SJacopo Mondi 3277641b044SJacopo Mondi clk = vco / (p4 + 1); 3286a26f141SJacopo Mondi enable = (clk > 70000) ? "X" : ""; 3297641b044SJacopo Mondi dev_dbg(&client->dev, "Sensor CLK : %10u K %s\n", clk, enable); 3307641b044SJacopo Mondi 3317641b044SJacopo Mondi clk = vco / (p7 + 1); 3327641b044SJacopo Mondi dev_dbg(&client->dev, "External sensor : %10u K\n", clk); 3337641b044SJacopo Mondi 3347641b044SJacopo Mondi clk = ext / (n + 1); 3356a26f141SJacopo Mondi enable = ((clk < 2000) || (clk > 24000)) ? "X" : ""; 3367641b044SJacopo Mondi dev_dbg(&client->dev, "PFD : %10u K %s\n", clk, enable); 3377641b044SJacopo Mondi 3387641b044SJacopo Mondi return 0; 3397641b044SJacopo Mondi } 3407641b044SJacopo Mondi #endif 3417641b044SJacopo Mondi 3427641b044SJacopo Mondi static int mt9t112_set_a_frame_size(const struct i2c_client *client, 3436a26f141SJacopo Mondi u16 width, u16 height) 3447641b044SJacopo Mondi { 3457641b044SJacopo Mondi int ret; 3467641b044SJacopo Mondi u16 wstart = (MAX_WIDTH - width) / 2; 3477641b044SJacopo Mondi u16 hstart = (MAX_HEIGHT - height) / 2; 3487641b044SJacopo Mondi 3496a26f141SJacopo Mondi /* (Context A) Image Width/Height. */ 3507641b044SJacopo Mondi mt9t112_mcu_write(ret, client, VAR(26, 0), width); 3517641b044SJacopo Mondi mt9t112_mcu_write(ret, client, VAR(26, 2), height); 3527641b044SJacopo Mondi 3536a26f141SJacopo Mondi /* (Context A) Output Width/Height. */ 3547641b044SJacopo Mondi mt9t112_mcu_write(ret, client, VAR(18, 43), 8 + width); 3557641b044SJacopo Mondi mt9t112_mcu_write(ret, client, VAR(18, 45), 8 + height); 3567641b044SJacopo Mondi 3576a26f141SJacopo Mondi /* (Context A) Start Row/Column. */ 3587641b044SJacopo Mondi mt9t112_mcu_write(ret, client, VAR(18, 2), 4 + hstart); 3597641b044SJacopo Mondi mt9t112_mcu_write(ret, client, VAR(18, 4), 4 + wstart); 3607641b044SJacopo Mondi 3616a26f141SJacopo Mondi /* (Context A) End Row/Column. */ 3627641b044SJacopo Mondi mt9t112_mcu_write(ret, client, VAR(18, 6), 11 + height + hstart); 3637641b044SJacopo Mondi mt9t112_mcu_write(ret, client, VAR(18, 8), 11 + width + wstart); 3647641b044SJacopo Mondi 3657641b044SJacopo Mondi mt9t112_mcu_write(ret, client, VAR8(1, 0), 0x06); 3667641b044SJacopo Mondi 3677641b044SJacopo Mondi return ret; 3687641b044SJacopo Mondi } 3697641b044SJacopo Mondi 3707641b044SJacopo Mondi static int mt9t112_set_pll_dividers(const struct i2c_client *client, 3716a26f141SJacopo Mondi u8 m, u8 n, u8 p1, u8 p2, u8 p3, u8 p4, 3726a26f141SJacopo Mondi u8 p5, u8 p6, u8 p7) 3737641b044SJacopo Mondi { 3747641b044SJacopo Mondi int ret; 3757641b044SJacopo Mondi u16 val; 3767641b044SJacopo Mondi 3777641b044SJacopo Mondi /* N/M */ 3786a26f141SJacopo Mondi val = (n << 8) | (m << 0); 3797641b044SJacopo Mondi mt9t112_reg_mask_set(ret, client, 0x0010, 0x3fff, val); 3807641b044SJacopo Mondi 3817641b044SJacopo Mondi /* P1/P2/P3 */ 3826a26f141SJacopo Mondi val = ((p3 & 0x0F) << 8) | ((p2 & 0x0F) << 4) | ((p1 & 0x0F) << 0); 3837641b044SJacopo Mondi mt9t112_reg_mask_set(ret, client, 0x0012, 0x0fff, val); 3847641b044SJacopo Mondi 3857641b044SJacopo Mondi /* P4/P5/P6 */ 3866a26f141SJacopo Mondi val = (0x7 << 12) | ((p6 & 0x0F) << 8) | ((p5 & 0x0F) << 4) | 3877641b044SJacopo Mondi ((p4 & 0x0F) << 0); 3887641b044SJacopo Mondi mt9t112_reg_mask_set(ret, client, 0x002A, 0x7fff, val); 3897641b044SJacopo Mondi 3907641b044SJacopo Mondi /* P7 */ 3916a26f141SJacopo Mondi val = (0x1 << 12) | ((p7 & 0x0F) << 0); 3927641b044SJacopo Mondi mt9t112_reg_mask_set(ret, client, 0x002C, 0x100f, val); 3937641b044SJacopo Mondi 3947641b044SJacopo Mondi return ret; 3957641b044SJacopo Mondi } 3967641b044SJacopo Mondi 3977641b044SJacopo Mondi static int mt9t112_init_pll(const struct i2c_client *client) 3987641b044SJacopo Mondi { 3997641b044SJacopo Mondi struct mt9t112_priv *priv = to_mt9t112(client); 4007641b044SJacopo Mondi int data, i, ret; 4017641b044SJacopo Mondi 4027641b044SJacopo Mondi mt9t112_reg_mask_set(ret, client, 0x0014, 0x003, 0x0001); 4037641b044SJacopo Mondi 4046a26f141SJacopo Mondi /* PLL control: BYPASS PLL = 8517. */ 4057641b044SJacopo Mondi mt9t112_reg_write(ret, client, 0x0014, 0x2145); 4067641b044SJacopo Mondi 4076a26f141SJacopo Mondi /* Replace these registers when new timing parameters are generated. */ 4087641b044SJacopo Mondi mt9t112_set_pll_dividers(client, 4096a26f141SJacopo Mondi priv->info->divider.m, priv->info->divider.n, 4106a26f141SJacopo Mondi priv->info->divider.p1, priv->info->divider.p2, 4116a26f141SJacopo Mondi priv->info->divider.p3, priv->info->divider.p4, 4126a26f141SJacopo Mondi priv->info->divider.p5, priv->info->divider.p6, 4137641b044SJacopo Mondi priv->info->divider.p7); 4147641b044SJacopo Mondi 4157641b044SJacopo Mondi /* 4167641b044SJacopo Mondi * TEST_BYPASS on 4177641b044SJacopo Mondi * PLL_ENABLE on 4187641b044SJacopo Mondi * SEL_LOCK_DET on 4197641b044SJacopo Mondi * TEST_BYPASS off 4207641b044SJacopo Mondi */ 4217641b044SJacopo Mondi mt9t112_reg_write(ret, client, 0x0014, 0x2525); 4227641b044SJacopo Mondi mt9t112_reg_write(ret, client, 0x0014, 0x2527); 4237641b044SJacopo Mondi mt9t112_reg_write(ret, client, 0x0014, 0x3427); 4247641b044SJacopo Mondi mt9t112_reg_write(ret, client, 0x0014, 0x3027); 4257641b044SJacopo Mondi 4267641b044SJacopo Mondi mdelay(10); 4277641b044SJacopo Mondi 4287641b044SJacopo Mondi /* 4297641b044SJacopo Mondi * PLL_BYPASS off 4307641b044SJacopo Mondi * Reference clock count 4317641b044SJacopo Mondi * I2C Master Clock Divider 4327641b044SJacopo Mondi */ 4337641b044SJacopo Mondi mt9t112_reg_write(ret, client, 0x0014, 0x3046); 4346a26f141SJacopo Mondi /* JPEG initialization workaround */ 4356a26f141SJacopo Mondi mt9t112_reg_write(ret, client, 0x0016, 0x0400); 4367641b044SJacopo Mondi mt9t112_reg_write(ret, client, 0x0022, 0x0190); 4377641b044SJacopo Mondi mt9t112_reg_write(ret, client, 0x3B84, 0x0212); 4387641b044SJacopo Mondi 4396a26f141SJacopo Mondi /* External sensor clock is PLL bypass. */ 4407641b044SJacopo Mondi mt9t112_reg_write(ret, client, 0x002E, 0x0500); 4417641b044SJacopo Mondi 4427641b044SJacopo Mondi mt9t112_reg_mask_set(ret, client, 0x0018, 0x0002, 0x0002); 4437641b044SJacopo Mondi mt9t112_reg_mask_set(ret, client, 0x3B82, 0x0004, 0x0004); 4447641b044SJacopo Mondi 4456a26f141SJacopo Mondi /* MCU disabled. */ 4467641b044SJacopo Mondi mt9t112_reg_mask_set(ret, client, 0x0018, 0x0004, 0x0004); 4477641b044SJacopo Mondi 4486a26f141SJacopo Mondi /* Out of standby. */ 4497641b044SJacopo Mondi mt9t112_reg_mask_set(ret, client, 0x0018, 0x0001, 0); 4507641b044SJacopo Mondi 4517641b044SJacopo Mondi mdelay(50); 4527641b044SJacopo Mondi 4537641b044SJacopo Mondi /* 4547641b044SJacopo Mondi * Standby Workaround 4557641b044SJacopo Mondi * Disable Secondary I2C Pads 4567641b044SJacopo Mondi */ 4577641b044SJacopo Mondi mt9t112_reg_write(ret, client, 0x0614, 0x0001); 4587641b044SJacopo Mondi mdelay(1); 4597641b044SJacopo Mondi mt9t112_reg_write(ret, client, 0x0614, 0x0001); 4607641b044SJacopo Mondi mdelay(1); 4617641b044SJacopo Mondi mt9t112_reg_write(ret, client, 0x0614, 0x0001); 4627641b044SJacopo Mondi mdelay(1); 4637641b044SJacopo Mondi mt9t112_reg_write(ret, client, 0x0614, 0x0001); 4647641b044SJacopo Mondi mdelay(1); 4657641b044SJacopo Mondi mt9t112_reg_write(ret, client, 0x0614, 0x0001); 4667641b044SJacopo Mondi mdelay(1); 4677641b044SJacopo Mondi mt9t112_reg_write(ret, client, 0x0614, 0x0001); 4687641b044SJacopo Mondi mdelay(1); 4697641b044SJacopo Mondi 4706a26f141SJacopo Mondi /* Poll to verify out of standby. Must Poll this bit. */ 4717641b044SJacopo Mondi for (i = 0; i < 100; i++) { 4727641b044SJacopo Mondi mt9t112_reg_read(data, client, 0x0018); 4736a26f141SJacopo Mondi if (!(data & 0x4000)) 4747641b044SJacopo Mondi break; 4757641b044SJacopo Mondi 4767641b044SJacopo Mondi mdelay(10); 4777641b044SJacopo Mondi } 4787641b044SJacopo Mondi 4797641b044SJacopo Mondi return ret; 4807641b044SJacopo Mondi } 4817641b044SJacopo Mondi 4827641b044SJacopo Mondi static int mt9t112_init_setting(const struct i2c_client *client) 4837641b044SJacopo Mondi { 4847641b044SJacopo Mondi int ret; 4857641b044SJacopo Mondi 4867641b044SJacopo Mondi /* Adaptive Output Clock (A) */ 4877641b044SJacopo Mondi mt9t112_mcu_mask_set(ret, client, VAR(26, 160), 0x0040, 0x0000); 4887641b044SJacopo Mondi 4897641b044SJacopo Mondi /* Read Mode (A) */ 4907641b044SJacopo Mondi mt9t112_mcu_write(ret, client, VAR(18, 12), 0x0024); 4917641b044SJacopo Mondi 4927641b044SJacopo Mondi /* Fine Correction (A) */ 4937641b044SJacopo Mondi mt9t112_mcu_write(ret, client, VAR(18, 15), 0x00CC); 4947641b044SJacopo Mondi 4957641b044SJacopo Mondi /* Fine IT Min (A) */ 4967641b044SJacopo Mondi mt9t112_mcu_write(ret, client, VAR(18, 17), 0x01f1); 4977641b044SJacopo Mondi 4987641b044SJacopo Mondi /* Fine IT Max Margin (A) */ 4997641b044SJacopo Mondi mt9t112_mcu_write(ret, client, VAR(18, 19), 0x00fF); 5007641b044SJacopo Mondi 5017641b044SJacopo Mondi /* Base Frame Lines (A) */ 5027641b044SJacopo Mondi mt9t112_mcu_write(ret, client, VAR(18, 29), 0x032D); 5037641b044SJacopo Mondi 5047641b044SJacopo Mondi /* Min Line Length (A) */ 5057641b044SJacopo Mondi mt9t112_mcu_write(ret, client, VAR(18, 31), 0x073a); 5067641b044SJacopo Mondi 5077641b044SJacopo Mondi /* Line Length (A) */ 5087641b044SJacopo Mondi mt9t112_mcu_write(ret, client, VAR(18, 37), 0x07d0); 5097641b044SJacopo Mondi 5107641b044SJacopo Mondi /* Adaptive Output Clock (B) */ 5117641b044SJacopo Mondi mt9t112_mcu_mask_set(ret, client, VAR(27, 160), 0x0040, 0x0000); 5127641b044SJacopo Mondi 5137641b044SJacopo Mondi /* Row Start (B) */ 5147641b044SJacopo Mondi mt9t112_mcu_write(ret, client, VAR(18, 74), 0x004); 5157641b044SJacopo Mondi 5167641b044SJacopo Mondi /* Column Start (B) */ 5177641b044SJacopo Mondi mt9t112_mcu_write(ret, client, VAR(18, 76), 0x004); 5187641b044SJacopo Mondi 5197641b044SJacopo Mondi /* Row End (B) */ 5207641b044SJacopo Mondi mt9t112_mcu_write(ret, client, VAR(18, 78), 0x60B); 5217641b044SJacopo Mondi 5227641b044SJacopo Mondi /* Column End (B) */ 5237641b044SJacopo Mondi mt9t112_mcu_write(ret, client, VAR(18, 80), 0x80B); 5247641b044SJacopo Mondi 5257641b044SJacopo Mondi /* Fine Correction (B) */ 5267641b044SJacopo Mondi mt9t112_mcu_write(ret, client, VAR(18, 87), 0x008C); 5277641b044SJacopo Mondi 5287641b044SJacopo Mondi /* Fine IT Min (B) */ 5297641b044SJacopo Mondi mt9t112_mcu_write(ret, client, VAR(18, 89), 0x01F1); 5307641b044SJacopo Mondi 5317641b044SJacopo Mondi /* Fine IT Max Margin (B) */ 5327641b044SJacopo Mondi mt9t112_mcu_write(ret, client, VAR(18, 91), 0x00FF); 5337641b044SJacopo Mondi 5347641b044SJacopo Mondi /* Base Frame Lines (B) */ 5357641b044SJacopo Mondi mt9t112_mcu_write(ret, client, VAR(18, 101), 0x0668); 5367641b044SJacopo Mondi 5377641b044SJacopo Mondi /* Min Line Length (B) */ 5387641b044SJacopo Mondi mt9t112_mcu_write(ret, client, VAR(18, 103), 0x0AF0); 5397641b044SJacopo Mondi 5407641b044SJacopo Mondi /* Line Length (B) */ 5417641b044SJacopo Mondi mt9t112_mcu_write(ret, client, VAR(18, 109), 0x0AF0); 5427641b044SJacopo Mondi 5437641b044SJacopo Mondi /* 5446a26f141SJacopo Mondi * Flicker Dectection registers. 5456a26f141SJacopo Mondi * This section should be replaced whenever new timing file is 5466a26f141SJacopo Mondi * generated. All the following registers need to be replaced. 5477641b044SJacopo Mondi * Following registers are generated from Register Wizard but user can 5486a26f141SJacopo Mondi * modify them. For detail see auto flicker detection tuning. 5497641b044SJacopo Mondi */ 5507641b044SJacopo Mondi 5517641b044SJacopo Mondi /* FD_FDPERIOD_SELECT */ 5527641b044SJacopo Mondi mt9t112_mcu_write(ret, client, VAR8(8, 5), 0x01); 5537641b044SJacopo Mondi 5547641b044SJacopo Mondi /* PRI_B_CONFIG_FD_ALGO_RUN */ 5557641b044SJacopo Mondi mt9t112_mcu_write(ret, client, VAR(27, 17), 0x0003); 5567641b044SJacopo Mondi 5577641b044SJacopo Mondi /* PRI_A_CONFIG_FD_ALGO_RUN */ 5587641b044SJacopo Mondi mt9t112_mcu_write(ret, client, VAR(26, 17), 0x0003); 5597641b044SJacopo Mondi 5607641b044SJacopo Mondi /* 5616a26f141SJacopo Mondi * AFD range detection tuning registers. 5627641b044SJacopo Mondi */ 5637641b044SJacopo Mondi 5646a26f141SJacopo Mondi /* Search_f1_50 */ 5657641b044SJacopo Mondi mt9t112_mcu_write(ret, client, VAR8(18, 165), 0x25); 5667641b044SJacopo Mondi 5676a26f141SJacopo Mondi /* Search_f2_50 */ 5687641b044SJacopo Mondi mt9t112_mcu_write(ret, client, VAR8(18, 166), 0x28); 5697641b044SJacopo Mondi 5706a26f141SJacopo Mondi /* Search_f1_60 */ 5717641b044SJacopo Mondi mt9t112_mcu_write(ret, client, VAR8(18, 167), 0x2C); 5727641b044SJacopo Mondi 5736a26f141SJacopo Mondi /* Search_f2_60 */ 5747641b044SJacopo Mondi mt9t112_mcu_write(ret, client, VAR8(18, 168), 0x2F); 5757641b044SJacopo Mondi 5766a26f141SJacopo Mondi /* Period_50Hz (A) */ 5777641b044SJacopo Mondi mt9t112_mcu_write(ret, client, VAR8(18, 68), 0xBA); 5787641b044SJacopo Mondi 5796a26f141SJacopo Mondi /* Secret register by Aptina. */ 5806a26f141SJacopo Mondi /* Period_50Hz (A MSB) */ 5817641b044SJacopo Mondi mt9t112_mcu_write(ret, client, VAR8(18, 303), 0x00); 5827641b044SJacopo Mondi 5836a26f141SJacopo Mondi /* Period_60Hz (A) */ 5847641b044SJacopo Mondi mt9t112_mcu_write(ret, client, VAR8(18, 69), 0x9B); 5857641b044SJacopo Mondi 5866a26f141SJacopo Mondi /* Secret register by Aptina. */ 5876a26f141SJacopo Mondi /* Period_60Hz (A MSB) */ 5887641b044SJacopo Mondi mt9t112_mcu_write(ret, client, VAR8(18, 301), 0x00); 5897641b044SJacopo Mondi 5906a26f141SJacopo Mondi /* Period_50Hz (B) */ 5917641b044SJacopo Mondi mt9t112_mcu_write(ret, client, VAR8(18, 140), 0x82); 5927641b044SJacopo Mondi 5936a26f141SJacopo Mondi /* Secret register by Aptina. */ 5946a26f141SJacopo Mondi /* Period_50Hz (B) MSB */ 5957641b044SJacopo Mondi mt9t112_mcu_write(ret, client, VAR8(18, 304), 0x00); 5967641b044SJacopo Mondi 5976a26f141SJacopo Mondi /* Period_60Hz (B) */ 5987641b044SJacopo Mondi mt9t112_mcu_write(ret, client, VAR8(18, 141), 0x6D); 5997641b044SJacopo Mondi 6006a26f141SJacopo Mondi /* Secret register by Aptina. */ 6016a26f141SJacopo Mondi /* Period_60Hz (B) MSB */ 6027641b044SJacopo Mondi mt9t112_mcu_write(ret, client, VAR8(18, 302), 0x00); 6037641b044SJacopo Mondi 6047641b044SJacopo Mondi /* FD Mode */ 6057641b044SJacopo Mondi mt9t112_mcu_write(ret, client, VAR8(8, 2), 0x10); 6067641b044SJacopo Mondi 6077641b044SJacopo Mondi /* Stat_min */ 6087641b044SJacopo Mondi mt9t112_mcu_write(ret, client, VAR8(8, 9), 0x02); 6097641b044SJacopo Mondi 6107641b044SJacopo Mondi /* Stat_max */ 6117641b044SJacopo Mondi mt9t112_mcu_write(ret, client, VAR8(8, 10), 0x03); 6127641b044SJacopo Mondi 6137641b044SJacopo Mondi /* Min_amplitude */ 6147641b044SJacopo Mondi mt9t112_mcu_write(ret, client, VAR8(8, 12), 0x0A); 6157641b044SJacopo Mondi 6167641b044SJacopo Mondi /* RX FIFO Watermark (A) */ 6177641b044SJacopo Mondi mt9t112_mcu_write(ret, client, VAR(18, 70), 0x0014); 6187641b044SJacopo Mondi 6197641b044SJacopo Mondi /* RX FIFO Watermark (B) */ 6207641b044SJacopo Mondi mt9t112_mcu_write(ret, client, VAR(18, 142), 0x0014); 6217641b044SJacopo Mondi 6227641b044SJacopo Mondi /* MCLK: 16MHz 6237641b044SJacopo Mondi * PCLK: 73MHz 6247641b044SJacopo Mondi * CorePixCLK: 36.5 MHz 6257641b044SJacopo Mondi */ 6267641b044SJacopo Mondi mt9t112_mcu_write(ret, client, VAR8(18, 0x0044), 133); 6277641b044SJacopo Mondi mt9t112_mcu_write(ret, client, VAR8(18, 0x0045), 110); 6287641b044SJacopo Mondi mt9t112_mcu_write(ret, client, VAR8(18, 0x008c), 130); 6297641b044SJacopo Mondi mt9t112_mcu_write(ret, client, VAR8(18, 0x008d), 108); 6307641b044SJacopo Mondi 6317641b044SJacopo Mondi mt9t112_mcu_write(ret, client, VAR8(18, 0x00A5), 27); 6327641b044SJacopo Mondi mt9t112_mcu_write(ret, client, VAR8(18, 0x00a6), 30); 6337641b044SJacopo Mondi mt9t112_mcu_write(ret, client, VAR8(18, 0x00a7), 32); 6347641b044SJacopo Mondi mt9t112_mcu_write(ret, client, VAR8(18, 0x00a8), 35); 6357641b044SJacopo Mondi 6367641b044SJacopo Mondi return ret; 6377641b044SJacopo Mondi } 6387641b044SJacopo Mondi 6397641b044SJacopo Mondi static int mt9t112_auto_focus_setting(const struct i2c_client *client) 6407641b044SJacopo Mondi { 6417641b044SJacopo Mondi int ret; 6427641b044SJacopo Mondi 6437641b044SJacopo Mondi mt9t112_mcu_write(ret, client, VAR(12, 13), 0x000F); 6447641b044SJacopo Mondi mt9t112_mcu_write(ret, client, VAR(12, 23), 0x0F0F); 6457641b044SJacopo Mondi mt9t112_mcu_write(ret, client, VAR8(1, 0), 0x06); 6467641b044SJacopo Mondi 6477641b044SJacopo Mondi mt9t112_reg_write(ret, client, 0x0614, 0x0000); 6487641b044SJacopo Mondi 6497641b044SJacopo Mondi mt9t112_mcu_write(ret, client, VAR8(1, 0), 0x05); 6507641b044SJacopo Mondi mt9t112_mcu_write(ret, client, VAR8(12, 2), 0x02); 6517641b044SJacopo Mondi mt9t112_mcu_write(ret, client, VAR(12, 3), 0x0002); 6527641b044SJacopo Mondi mt9t112_mcu_write(ret, client, VAR(17, 3), 0x8001); 6537641b044SJacopo Mondi mt9t112_mcu_write(ret, client, VAR(17, 11), 0x0025); 6547641b044SJacopo Mondi mt9t112_mcu_write(ret, client, VAR(17, 13), 0x0193); 6557641b044SJacopo Mondi mt9t112_mcu_write(ret, client, VAR8(17, 33), 0x18); 6567641b044SJacopo Mondi mt9t112_mcu_write(ret, client, VAR8(1, 0), 0x05); 6577641b044SJacopo Mondi 6587641b044SJacopo Mondi return ret; 6597641b044SJacopo Mondi } 6607641b044SJacopo Mondi 6617641b044SJacopo Mondi static int mt9t112_auto_focus_trigger(const struct i2c_client *client) 6627641b044SJacopo Mondi { 6637641b044SJacopo Mondi int ret; 6647641b044SJacopo Mondi 6657641b044SJacopo Mondi mt9t112_mcu_write(ret, client, VAR8(12, 25), 0x01); 6667641b044SJacopo Mondi 6677641b044SJacopo Mondi return ret; 6687641b044SJacopo Mondi } 6697641b044SJacopo Mondi 6707641b044SJacopo Mondi static int mt9t112_init_camera(const struct i2c_client *client) 6717641b044SJacopo Mondi { 6727641b044SJacopo Mondi int ret; 6737641b044SJacopo Mondi 6747641b044SJacopo Mondi ECHECKER(ret, mt9t112_reset(client)); 6757641b044SJacopo Mondi ECHECKER(ret, mt9t112_init_pll(client)); 6767641b044SJacopo Mondi ECHECKER(ret, mt9t112_init_setting(client)); 6777641b044SJacopo Mondi ECHECKER(ret, mt9t112_auto_focus_setting(client)); 6787641b044SJacopo Mondi 6797641b044SJacopo Mondi mt9t112_reg_mask_set(ret, client, 0x0018, 0x0004, 0); 6807641b044SJacopo Mondi 6816a26f141SJacopo Mondi /* Analog setting B.*/ 6827641b044SJacopo Mondi mt9t112_reg_write(ret, client, 0x3084, 0x2409); 6837641b044SJacopo Mondi mt9t112_reg_write(ret, client, 0x3092, 0x0A49); 6847641b044SJacopo Mondi mt9t112_reg_write(ret, client, 0x3094, 0x4949); 6857641b044SJacopo Mondi mt9t112_reg_write(ret, client, 0x3096, 0x4950); 6867641b044SJacopo Mondi 6877641b044SJacopo Mondi /* 6886a26f141SJacopo Mondi * Disable adaptive clock. 6897641b044SJacopo Mondi * PRI_A_CONFIG_JPEG_OB_TX_CONTROL_VAR 6907641b044SJacopo Mondi * PRI_B_CONFIG_JPEG_OB_TX_CONTROL_VAR 6917641b044SJacopo Mondi */ 6927641b044SJacopo Mondi mt9t112_mcu_write(ret, client, VAR(26, 160), 0x0A2E); 6937641b044SJacopo Mondi mt9t112_mcu_write(ret, client, VAR(27, 160), 0x0A2E); 6947641b044SJacopo Mondi 6956a26f141SJacopo Mondi /* 6966a26f141SJacopo Mondi * Configure Status in Status_before_length Format and enable header. 6976a26f141SJacopo Mondi * PRI_B_CONFIG_JPEG_OB_TX_CONTROL_VAR 6986a26f141SJacopo Mondi */ 6997641b044SJacopo Mondi mt9t112_mcu_write(ret, client, VAR(27, 144), 0x0CB4); 7007641b044SJacopo Mondi 7016a26f141SJacopo Mondi /* 7026a26f141SJacopo Mondi * Enable JPEG in context B. 7036a26f141SJacopo Mondi * PRI_B_CONFIG_JPEG_OB_TX_CONTROL_VAR 7046a26f141SJacopo Mondi */ 7057641b044SJacopo Mondi mt9t112_mcu_write(ret, client, VAR8(27, 142), 0x01); 7067641b044SJacopo Mondi 7076a26f141SJacopo Mondi /* Disable Dac_TXLO. */ 7087641b044SJacopo Mondi mt9t112_reg_write(ret, client, 0x316C, 0x350F); 7097641b044SJacopo Mondi 7106a26f141SJacopo Mondi /* Set max slew rates. */ 7117641b044SJacopo Mondi mt9t112_reg_write(ret, client, 0x1E, 0x777); 7127641b044SJacopo Mondi 7137641b044SJacopo Mondi return ret; 7147641b044SJacopo Mondi } 7157641b044SJacopo Mondi 7167641b044SJacopo Mondi /************************************************************************ 7176a26f141SJacopo Mondi * v4l2_subdev_core_ops 7186a26f141SJacopo Mondi ***********************************************************************/ 7197641b044SJacopo Mondi 7207641b044SJacopo Mondi #ifdef CONFIG_VIDEO_ADV_DEBUG 7217641b044SJacopo Mondi static int mt9t112_g_register(struct v4l2_subdev *sd, 7227641b044SJacopo Mondi struct v4l2_dbg_register *reg) 7237641b044SJacopo Mondi { 7247641b044SJacopo Mondi struct i2c_client *client = v4l2_get_subdevdata(sd); 7257641b044SJacopo Mondi int ret; 7267641b044SJacopo Mondi 7277641b044SJacopo Mondi reg->size = 2; 7287641b044SJacopo Mondi mt9t112_reg_read(ret, client, reg->reg); 7297641b044SJacopo Mondi 7307641b044SJacopo Mondi reg->val = (__u64)ret; 7317641b044SJacopo Mondi 7327641b044SJacopo Mondi return 0; 7337641b044SJacopo Mondi } 7347641b044SJacopo Mondi 7357641b044SJacopo Mondi static int mt9t112_s_register(struct v4l2_subdev *sd, 7367641b044SJacopo Mondi const struct v4l2_dbg_register *reg) 7377641b044SJacopo Mondi { 7387641b044SJacopo Mondi struct i2c_client *client = v4l2_get_subdevdata(sd); 7397641b044SJacopo Mondi int ret; 7407641b044SJacopo Mondi 7417641b044SJacopo Mondi mt9t112_reg_write(ret, client, reg->reg, reg->val); 7427641b044SJacopo Mondi 7437641b044SJacopo Mondi return ret; 7447641b044SJacopo Mondi } 7457641b044SJacopo Mondi #endif 7467641b044SJacopo Mondi 7476a26f141SJacopo Mondi static int mt9t112_power_on(struct mt9t112_priv *priv) 7486a26f141SJacopo Mondi { 7496a26f141SJacopo Mondi int ret; 7506a26f141SJacopo Mondi 7516a26f141SJacopo Mondi ret = clk_prepare_enable(priv->clk); 7526a26f141SJacopo Mondi if (ret) 7536a26f141SJacopo Mondi return ret; 7546a26f141SJacopo Mondi 7556a26f141SJacopo Mondi if (priv->standby_gpio) { 7566a26f141SJacopo Mondi gpiod_set_value(priv->standby_gpio, 0); 7576a26f141SJacopo Mondi msleep(100); 7586a26f141SJacopo Mondi } 7596a26f141SJacopo Mondi 7606a26f141SJacopo Mondi return 0; 7616a26f141SJacopo Mondi } 7626a26f141SJacopo Mondi 7636a26f141SJacopo Mondi static int mt9t112_power_off(struct mt9t112_priv *priv) 7646a26f141SJacopo Mondi { 7656a26f141SJacopo Mondi clk_disable_unprepare(priv->clk); 7666a26f141SJacopo Mondi if (priv->standby_gpio) { 7676a26f141SJacopo Mondi gpiod_set_value(priv->standby_gpio, 1); 7686a26f141SJacopo Mondi msleep(100); 7696a26f141SJacopo Mondi } 7706a26f141SJacopo Mondi 7716a26f141SJacopo Mondi return 0; 7726a26f141SJacopo Mondi } 7736a26f141SJacopo Mondi 7747641b044SJacopo Mondi static int mt9t112_s_power(struct v4l2_subdev *sd, int on) 7757641b044SJacopo Mondi { 7767641b044SJacopo Mondi struct i2c_client *client = v4l2_get_subdevdata(sd); 7777641b044SJacopo Mondi struct mt9t112_priv *priv = to_mt9t112(client); 7787641b044SJacopo Mondi 7796a26f141SJacopo Mondi return on ? mt9t112_power_on(priv) : 7806a26f141SJacopo Mondi mt9t112_power_off(priv); 7817641b044SJacopo Mondi } 7827641b044SJacopo Mondi 7837641b044SJacopo Mondi static const struct v4l2_subdev_core_ops mt9t112_subdev_core_ops = { 7847641b044SJacopo Mondi #ifdef CONFIG_VIDEO_ADV_DEBUG 7857641b044SJacopo Mondi .g_register = mt9t112_g_register, 7867641b044SJacopo Mondi .s_register = mt9t112_s_register, 7877641b044SJacopo Mondi #endif 7887641b044SJacopo Mondi .s_power = mt9t112_s_power, 7897641b044SJacopo Mondi }; 7907641b044SJacopo Mondi 7917641b044SJacopo Mondi /************************************************************************ 7926a26f141SJacopo Mondi * v4l2_subdev_video_ops 7936a26f141SJacopo Mondi **********************************************************************/ 7947641b044SJacopo Mondi static int mt9t112_s_stream(struct v4l2_subdev *sd, int enable) 7957641b044SJacopo Mondi { 7967641b044SJacopo Mondi struct i2c_client *client = v4l2_get_subdevdata(sd); 7977641b044SJacopo Mondi struct mt9t112_priv *priv = to_mt9t112(client); 7987641b044SJacopo Mondi int ret = 0; 7997641b044SJacopo Mondi 8007641b044SJacopo Mondi if (!enable) { 8017641b044SJacopo Mondi /* FIXME 8027641b044SJacopo Mondi * 8036a26f141SJacopo Mondi * If user selected large output size, and used it long time, 8047641b044SJacopo Mondi * mt9t112 camera will be very warm. 8057641b044SJacopo Mondi * 8067641b044SJacopo Mondi * But current driver can not stop mt9t112 camera. 8077641b044SJacopo Mondi * So, set small size here to solve this problem. 8087641b044SJacopo Mondi */ 8097641b044SJacopo Mondi mt9t112_set_a_frame_size(client, VGA_WIDTH, VGA_HEIGHT); 8107641b044SJacopo Mondi return ret; 8117641b044SJacopo Mondi } 8127641b044SJacopo Mondi 8136a26f141SJacopo Mondi if (!priv->init_done) { 8146a26f141SJacopo Mondi u16 param = MT9T112_FLAG_PCLK_RISING_EDGE & priv->info->flags ? 8156a26f141SJacopo Mondi 0x0001 : 0x0000; 8167641b044SJacopo Mondi 8177641b044SJacopo Mondi ECHECKER(ret, mt9t112_init_camera(client)); 8187641b044SJacopo Mondi 8196a26f141SJacopo Mondi /* Invert PCLK (Data sampled on falling edge of pixclk). */ 8207641b044SJacopo Mondi mt9t112_reg_write(ret, client, 0x3C20, param); 8217641b044SJacopo Mondi 8227641b044SJacopo Mondi mdelay(5); 8237641b044SJacopo Mondi 8246a26f141SJacopo Mondi priv->init_done = true; 8257641b044SJacopo Mondi } 8267641b044SJacopo Mondi 8277641b044SJacopo Mondi mt9t112_mcu_write(ret, client, VAR(26, 7), priv->format->fmt); 8287641b044SJacopo Mondi mt9t112_mcu_write(ret, client, VAR(26, 9), priv->format->order); 8297641b044SJacopo Mondi mt9t112_mcu_write(ret, client, VAR8(1, 0), 0x06); 8307641b044SJacopo Mondi 8316a26f141SJacopo Mondi mt9t112_set_a_frame_size(client, priv->frame.width, priv->frame.height); 8327641b044SJacopo Mondi 8337641b044SJacopo Mondi ECHECKER(ret, mt9t112_auto_focus_trigger(client)); 8347641b044SJacopo Mondi 8357641b044SJacopo Mondi dev_dbg(&client->dev, "format : %d\n", priv->format->code); 8367641b044SJacopo Mondi dev_dbg(&client->dev, "size : %d x %d\n", 8377641b044SJacopo Mondi priv->frame.width, 8387641b044SJacopo Mondi priv->frame.height); 8397641b044SJacopo Mondi 8407641b044SJacopo Mondi CLOCK_INFO(client, EXT_CLOCK); 8417641b044SJacopo Mondi 8427641b044SJacopo Mondi return ret; 8437641b044SJacopo Mondi } 8447641b044SJacopo Mondi 8457641b044SJacopo Mondi static int mt9t112_set_params(struct mt9t112_priv *priv, 8467641b044SJacopo Mondi const struct v4l2_rect *rect, 8477641b044SJacopo Mondi u32 code) 8487641b044SJacopo Mondi { 8497641b044SJacopo Mondi int i; 8507641b044SJacopo Mondi 8517641b044SJacopo Mondi /* 8527641b044SJacopo Mondi * get color format 8537641b044SJacopo Mondi */ 8547641b044SJacopo Mondi for (i = 0; i < priv->num_formats; i++) 8557641b044SJacopo Mondi if (mt9t112_cfmts[i].code == code) 8567641b044SJacopo Mondi break; 8577641b044SJacopo Mondi 8587641b044SJacopo Mondi if (i == priv->num_formats) 8597641b044SJacopo Mondi return -EINVAL; 8607641b044SJacopo Mondi 8617641b044SJacopo Mondi priv->frame = *rect; 8627641b044SJacopo Mondi 8637641b044SJacopo Mondi /* 8647641b044SJacopo Mondi * frame size check 8657641b044SJacopo Mondi */ 8666a26f141SJacopo Mondi v4l_bound_align_image(&priv->frame.width, 0, MAX_WIDTH, 0, 8676a26f141SJacopo Mondi &priv->frame.height, 0, MAX_HEIGHT, 0, 0); 8687641b044SJacopo Mondi 8697641b044SJacopo Mondi priv->format = mt9t112_cfmts + i; 8707641b044SJacopo Mondi 8717641b044SJacopo Mondi return 0; 8727641b044SJacopo Mondi } 8737641b044SJacopo Mondi 8747641b044SJacopo Mondi static int mt9t112_get_selection(struct v4l2_subdev *sd, 8757641b044SJacopo Mondi struct v4l2_subdev_pad_config *cfg, 8767641b044SJacopo Mondi struct v4l2_subdev_selection *sel) 8777641b044SJacopo Mondi { 8787641b044SJacopo Mondi struct i2c_client *client = v4l2_get_subdevdata(sd); 8797641b044SJacopo Mondi struct mt9t112_priv *priv = to_mt9t112(client); 8807641b044SJacopo Mondi 8817641b044SJacopo Mondi if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE) 8827641b044SJacopo Mondi return -EINVAL; 8837641b044SJacopo Mondi 8847641b044SJacopo Mondi switch (sel->target) { 8857641b044SJacopo Mondi case V4L2_SEL_TGT_CROP_BOUNDS: 8867641b044SJacopo Mondi sel->r.left = 0; 8877641b044SJacopo Mondi sel->r.top = 0; 8887641b044SJacopo Mondi sel->r.width = MAX_WIDTH; 8897641b044SJacopo Mondi sel->r.height = MAX_HEIGHT; 8907641b044SJacopo Mondi return 0; 8917641b044SJacopo Mondi case V4L2_SEL_TGT_CROP_DEFAULT: 8927641b044SJacopo Mondi sel->r.left = 0; 8937641b044SJacopo Mondi sel->r.top = 0; 8947641b044SJacopo Mondi sel->r.width = VGA_WIDTH; 8957641b044SJacopo Mondi sel->r.height = VGA_HEIGHT; 8967641b044SJacopo Mondi return 0; 8977641b044SJacopo Mondi case V4L2_SEL_TGT_CROP: 8987641b044SJacopo Mondi sel->r = priv->frame; 8997641b044SJacopo Mondi return 0; 9007641b044SJacopo Mondi default: 9017641b044SJacopo Mondi return -EINVAL; 9027641b044SJacopo Mondi } 9037641b044SJacopo Mondi } 9047641b044SJacopo Mondi 9057641b044SJacopo Mondi static int mt9t112_set_selection(struct v4l2_subdev *sd, 9067641b044SJacopo Mondi struct v4l2_subdev_pad_config *cfg, 9077641b044SJacopo Mondi struct v4l2_subdev_selection *sel) 9087641b044SJacopo Mondi { 9097641b044SJacopo Mondi struct i2c_client *client = v4l2_get_subdevdata(sd); 9107641b044SJacopo Mondi struct mt9t112_priv *priv = to_mt9t112(client); 9117641b044SJacopo Mondi const struct v4l2_rect *rect = &sel->r; 9127641b044SJacopo Mondi 9137641b044SJacopo Mondi if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE || 9147641b044SJacopo Mondi sel->target != V4L2_SEL_TGT_CROP) 9157641b044SJacopo Mondi return -EINVAL; 9167641b044SJacopo Mondi 9177641b044SJacopo Mondi return mt9t112_set_params(priv, rect, priv->format->code); 9187641b044SJacopo Mondi } 9197641b044SJacopo Mondi 9207641b044SJacopo Mondi static int mt9t112_get_fmt(struct v4l2_subdev *sd, 9217641b044SJacopo Mondi struct v4l2_subdev_pad_config *cfg, 9227641b044SJacopo Mondi struct v4l2_subdev_format *format) 9237641b044SJacopo Mondi { 9247641b044SJacopo Mondi struct v4l2_mbus_framefmt *mf = &format->format; 9257641b044SJacopo Mondi struct i2c_client *client = v4l2_get_subdevdata(sd); 9267641b044SJacopo Mondi struct mt9t112_priv *priv = to_mt9t112(client); 9277641b044SJacopo Mondi 9287641b044SJacopo Mondi if (format->pad) 9297641b044SJacopo Mondi return -EINVAL; 9307641b044SJacopo Mondi 9317641b044SJacopo Mondi mf->width = priv->frame.width; 9327641b044SJacopo Mondi mf->height = priv->frame.height; 9337641b044SJacopo Mondi mf->colorspace = priv->format->colorspace; 9347641b044SJacopo Mondi mf->code = priv->format->code; 9357641b044SJacopo Mondi mf->field = V4L2_FIELD_NONE; 9367641b044SJacopo Mondi 9377641b044SJacopo Mondi return 0; 9387641b044SJacopo Mondi } 9397641b044SJacopo Mondi 9407641b044SJacopo Mondi static int mt9t112_s_fmt(struct v4l2_subdev *sd, 9417641b044SJacopo Mondi struct v4l2_mbus_framefmt *mf) 9427641b044SJacopo Mondi { 9437641b044SJacopo Mondi struct i2c_client *client = v4l2_get_subdevdata(sd); 9447641b044SJacopo Mondi struct mt9t112_priv *priv = to_mt9t112(client); 9457641b044SJacopo Mondi struct v4l2_rect rect = { 9467641b044SJacopo Mondi .width = mf->width, 9477641b044SJacopo Mondi .height = mf->height, 9487641b044SJacopo Mondi .left = priv->frame.left, 9497641b044SJacopo Mondi .top = priv->frame.top, 9507641b044SJacopo Mondi }; 9517641b044SJacopo Mondi int ret; 9527641b044SJacopo Mondi 9537641b044SJacopo Mondi ret = mt9t112_set_params(priv, &rect, mf->code); 9547641b044SJacopo Mondi 9557641b044SJacopo Mondi if (!ret) 9567641b044SJacopo Mondi mf->colorspace = priv->format->colorspace; 9577641b044SJacopo Mondi 9587641b044SJacopo Mondi return ret; 9597641b044SJacopo Mondi } 9607641b044SJacopo Mondi 9617641b044SJacopo Mondi static int mt9t112_set_fmt(struct v4l2_subdev *sd, 9627641b044SJacopo Mondi struct v4l2_subdev_pad_config *cfg, 9637641b044SJacopo Mondi struct v4l2_subdev_format *format) 9647641b044SJacopo Mondi { 9657641b044SJacopo Mondi struct i2c_client *client = v4l2_get_subdevdata(sd); 9666a26f141SJacopo Mondi struct v4l2_mbus_framefmt *mf = &format->format; 9677641b044SJacopo Mondi struct mt9t112_priv *priv = to_mt9t112(client); 9687641b044SJacopo Mondi int i; 9697641b044SJacopo Mondi 9707641b044SJacopo Mondi if (format->pad) 9717641b044SJacopo Mondi return -EINVAL; 9727641b044SJacopo Mondi 9737641b044SJacopo Mondi for (i = 0; i < priv->num_formats; i++) 9747641b044SJacopo Mondi if (mt9t112_cfmts[i].code == mf->code) 9757641b044SJacopo Mondi break; 9767641b044SJacopo Mondi 9777641b044SJacopo Mondi if (i == priv->num_formats) { 9787641b044SJacopo Mondi mf->code = MEDIA_BUS_FMT_UYVY8_2X8; 9797641b044SJacopo Mondi mf->colorspace = V4L2_COLORSPACE_JPEG; 9807641b044SJacopo Mondi } else { 9817641b044SJacopo Mondi mf->colorspace = mt9t112_cfmts[i].colorspace; 9827641b044SJacopo Mondi } 9837641b044SJacopo Mondi 9846a26f141SJacopo Mondi v4l_bound_align_image(&mf->width, 0, MAX_WIDTH, 0, 9856a26f141SJacopo Mondi &mf->height, 0, MAX_HEIGHT, 0, 0); 9867641b044SJacopo Mondi 9877641b044SJacopo Mondi mf->field = V4L2_FIELD_NONE; 9887641b044SJacopo Mondi 9897641b044SJacopo Mondi if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) 9907641b044SJacopo Mondi return mt9t112_s_fmt(sd, mf); 9917641b044SJacopo Mondi cfg->try_fmt = *mf; 9926a26f141SJacopo Mondi 9937641b044SJacopo Mondi return 0; 9947641b044SJacopo Mondi } 9957641b044SJacopo Mondi 9967641b044SJacopo Mondi static int mt9t112_enum_mbus_code(struct v4l2_subdev *sd, 9977641b044SJacopo Mondi struct v4l2_subdev_pad_config *cfg, 9987641b044SJacopo Mondi struct v4l2_subdev_mbus_code_enum *code) 9997641b044SJacopo Mondi { 10007641b044SJacopo Mondi struct i2c_client *client = v4l2_get_subdevdata(sd); 10017641b044SJacopo Mondi struct mt9t112_priv *priv = to_mt9t112(client); 10027641b044SJacopo Mondi 10037641b044SJacopo Mondi if (code->pad || code->index >= priv->num_formats) 10047641b044SJacopo Mondi return -EINVAL; 10057641b044SJacopo Mondi 10067641b044SJacopo Mondi code->code = mt9t112_cfmts[code->index].code; 10077641b044SJacopo Mondi 10087641b044SJacopo Mondi return 0; 10097641b044SJacopo Mondi } 10107641b044SJacopo Mondi 10117641b044SJacopo Mondi static const struct v4l2_subdev_video_ops mt9t112_subdev_video_ops = { 10127641b044SJacopo Mondi .s_stream = mt9t112_s_stream, 10137641b044SJacopo Mondi }; 10147641b044SJacopo Mondi 10157641b044SJacopo Mondi static const struct v4l2_subdev_pad_ops mt9t112_subdev_pad_ops = { 10167641b044SJacopo Mondi .enum_mbus_code = mt9t112_enum_mbus_code, 10177641b044SJacopo Mondi .get_selection = mt9t112_get_selection, 10187641b044SJacopo Mondi .set_selection = mt9t112_set_selection, 10197641b044SJacopo Mondi .get_fmt = mt9t112_get_fmt, 10207641b044SJacopo Mondi .set_fmt = mt9t112_set_fmt, 10217641b044SJacopo Mondi }; 10227641b044SJacopo Mondi 10237641b044SJacopo Mondi /************************************************************************ 10246a26f141SJacopo Mondi * i2c driver 10256a26f141SJacopo Mondi ***********************************************************************/ 10267641b044SJacopo Mondi static const struct v4l2_subdev_ops mt9t112_subdev_ops = { 10277641b044SJacopo Mondi .core = &mt9t112_subdev_core_ops, 10287641b044SJacopo Mondi .video = &mt9t112_subdev_video_ops, 10297641b044SJacopo Mondi .pad = &mt9t112_subdev_pad_ops, 10307641b044SJacopo Mondi }; 10317641b044SJacopo Mondi 10327641b044SJacopo Mondi static int mt9t112_camera_probe(struct i2c_client *client) 10337641b044SJacopo Mondi { 10347641b044SJacopo Mondi struct mt9t112_priv *priv = to_mt9t112(client); 10357641b044SJacopo Mondi const char *devname; 10367641b044SJacopo Mondi int chipid; 10377641b044SJacopo Mondi int ret; 10387641b044SJacopo Mondi 10397641b044SJacopo Mondi ret = mt9t112_s_power(&priv->subdev, 1); 10407641b044SJacopo Mondi if (ret < 0) 10417641b044SJacopo Mondi return ret; 10427641b044SJacopo Mondi 10436a26f141SJacopo Mondi /* Check and show chip ID. */ 10447641b044SJacopo Mondi mt9t112_reg_read(chipid, client, 0x0000); 10457641b044SJacopo Mondi 10467641b044SJacopo Mondi switch (chipid) { 10477641b044SJacopo Mondi case 0x2680: 10487641b044SJacopo Mondi devname = "mt9t111"; 10497641b044SJacopo Mondi priv->num_formats = 1; 10507641b044SJacopo Mondi break; 10517641b044SJacopo Mondi case 0x2682: 10527641b044SJacopo Mondi devname = "mt9t112"; 10537641b044SJacopo Mondi priv->num_formats = ARRAY_SIZE(mt9t112_cfmts); 10547641b044SJacopo Mondi break; 10557641b044SJacopo Mondi default: 10567641b044SJacopo Mondi dev_err(&client->dev, "Product ID error %04x\n", chipid); 10577641b044SJacopo Mondi ret = -ENODEV; 10587641b044SJacopo Mondi goto done; 10597641b044SJacopo Mondi } 10607641b044SJacopo Mondi 10617641b044SJacopo Mondi dev_info(&client->dev, "%s chip ID %04x\n", devname, chipid); 10627641b044SJacopo Mondi 10637641b044SJacopo Mondi done: 10647641b044SJacopo Mondi mt9t112_s_power(&priv->subdev, 0); 10656a26f141SJacopo Mondi 10667641b044SJacopo Mondi return ret; 10677641b044SJacopo Mondi } 10687641b044SJacopo Mondi 10697641b044SJacopo Mondi static int mt9t112_probe(struct i2c_client *client, 10707641b044SJacopo Mondi const struct i2c_device_id *did) 10717641b044SJacopo Mondi { 10727641b044SJacopo Mondi struct mt9t112_priv *priv; 10737641b044SJacopo Mondi int ret; 10747641b044SJacopo Mondi 10756a26f141SJacopo Mondi if (!client->dev.platform_data) { 10767641b044SJacopo Mondi dev_err(&client->dev, "mt9t112: missing platform data!\n"); 10777641b044SJacopo Mondi return -EINVAL; 10787641b044SJacopo Mondi } 10797641b044SJacopo Mondi 10807641b044SJacopo Mondi priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL); 10817641b044SJacopo Mondi if (!priv) 10827641b044SJacopo Mondi return -ENOMEM; 10837641b044SJacopo Mondi 10846a26f141SJacopo Mondi priv->info = client->dev.platform_data; 10856a26f141SJacopo Mondi priv->init_done = false; 10867641b044SJacopo Mondi 10877641b044SJacopo Mondi v4l2_i2c_subdev_init(&priv->subdev, client, &mt9t112_subdev_ops); 10887641b044SJacopo Mondi 10896a26f141SJacopo Mondi priv->clk = devm_clk_get(&client->dev, "extclk"); 10906a26f141SJacopo Mondi if (PTR_ERR(priv->clk) == -ENOENT) { 10916a26f141SJacopo Mondi priv->clk = NULL; 10926a26f141SJacopo Mondi } else if (IS_ERR(priv->clk)) { 10936a26f141SJacopo Mondi dev_err(&client->dev, "Unable to get clock \"extclk\"\n"); 10947641b044SJacopo Mondi return PTR_ERR(priv->clk); 10956a26f141SJacopo Mondi } 10966a26f141SJacopo Mondi 10976a26f141SJacopo Mondi priv->standby_gpio = devm_gpiod_get_optional(&client->dev, "standby", 10986a26f141SJacopo Mondi GPIOD_OUT_HIGH); 10996a26f141SJacopo Mondi if (IS_ERR(priv->standby_gpio)) { 11006a26f141SJacopo Mondi dev_err(&client->dev, "Unable to get gpio \"standby\"\n"); 11016a26f141SJacopo Mondi return PTR_ERR(priv->standby_gpio); 11026a26f141SJacopo Mondi } 11037641b044SJacopo Mondi 11047641b044SJacopo Mondi ret = mt9t112_camera_probe(client); 11056a26f141SJacopo Mondi if (ret) 11067641b044SJacopo Mondi return ret; 11076a26f141SJacopo Mondi 11086a26f141SJacopo Mondi return v4l2_async_register_subdev(&priv->subdev); 11097641b044SJacopo Mondi } 11107641b044SJacopo Mondi 11117641b044SJacopo Mondi static int mt9t112_remove(struct i2c_client *client) 11127641b044SJacopo Mondi { 11137641b044SJacopo Mondi struct mt9t112_priv *priv = to_mt9t112(client); 11147641b044SJacopo Mondi 11156a26f141SJacopo Mondi clk_disable_unprepare(priv->clk); 11166a26f141SJacopo Mondi v4l2_async_unregister_subdev(&priv->subdev); 11176a26f141SJacopo Mondi 11187641b044SJacopo Mondi return 0; 11197641b044SJacopo Mondi } 11207641b044SJacopo Mondi 11217641b044SJacopo Mondi static const struct i2c_device_id mt9t112_id[] = { 11227641b044SJacopo Mondi { "mt9t112", 0 }, 11237641b044SJacopo Mondi { } 11247641b044SJacopo Mondi }; 11257641b044SJacopo Mondi MODULE_DEVICE_TABLE(i2c, mt9t112_id); 11267641b044SJacopo Mondi 11277641b044SJacopo Mondi static struct i2c_driver mt9t112_i2c_driver = { 11287641b044SJacopo Mondi .driver = { 11297641b044SJacopo Mondi .name = "mt9t112", 11307641b044SJacopo Mondi }, 11317641b044SJacopo Mondi .probe = mt9t112_probe, 11327641b044SJacopo Mondi .remove = mt9t112_remove, 11337641b044SJacopo Mondi .id_table = mt9t112_id, 11347641b044SJacopo Mondi }; 11357641b044SJacopo Mondi 11367641b044SJacopo Mondi module_i2c_driver(mt9t112_i2c_driver); 11377641b044SJacopo Mondi 11386a26f141SJacopo Mondi MODULE_DESCRIPTION("V4L2 driver for MT9T111/MT9T112 camera sensor"); 11397641b044SJacopo Mondi MODULE_AUTHOR("Kuninori Morimoto"); 11407641b044SJacopo Mondi MODULE_LICENSE("GPL v2"); 1141