18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * OmniVision OV96xx Camera Driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2009 Marek Vasut <marek.vasut@gmail.com> 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Based on ov772x camera driver: 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * Copyright (C) 2008 Renesas Solutions Corp. 108c2ecf20Sopenharmony_ci * Kuninori Morimoto <morimoto.kuninori@renesas.com> 118c2ecf20Sopenharmony_ci * 128c2ecf20Sopenharmony_ci * Based on ov7670 and soc_camera_platform driver, 138c2ecf20Sopenharmony_ci * transition from soc_camera to pxa_camera based on mt9m111 148c2ecf20Sopenharmony_ci * 158c2ecf20Sopenharmony_ci * Copyright 2006-7 Jonathan Corbet <corbet@lwn.net> 168c2ecf20Sopenharmony_ci * Copyright (C) 2008 Magnus Damm 178c2ecf20Sopenharmony_ci * Copyright (C) 2008, Guennadi Liakhovetski <kernel@pengutronix.de> 188c2ecf20Sopenharmony_ci */ 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#include <linux/init.h> 218c2ecf20Sopenharmony_ci#include <linux/module.h> 228c2ecf20Sopenharmony_ci#include <linux/i2c.h> 238c2ecf20Sopenharmony_ci#include <linux/slab.h> 248c2ecf20Sopenharmony_ci#include <linux/delay.h> 258c2ecf20Sopenharmony_ci#include <linux/v4l2-mediabus.h> 268c2ecf20Sopenharmony_ci#include <linux/videodev2.h> 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci#include <media/v4l2-async.h> 298c2ecf20Sopenharmony_ci#include <media/v4l2-clk.h> 308c2ecf20Sopenharmony_ci#include <media/v4l2-common.h> 318c2ecf20Sopenharmony_ci#include <media/v4l2-ctrls.h> 328c2ecf20Sopenharmony_ci#include <media/v4l2-device.h> 338c2ecf20Sopenharmony_ci#include <media/v4l2-event.h> 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci#include <linux/gpio/consumer.h> 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci#include "ov9640.h" 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci#define to_ov9640_sensor(sd) container_of(sd, struct ov9640_priv, subdev) 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci/* default register setup */ 428c2ecf20Sopenharmony_cistatic const struct ov9640_reg ov9640_regs_dflt[] = { 438c2ecf20Sopenharmony_ci { OV9640_COM5, OV9640_COM5_SYSCLK | OV9640_COM5_LONGEXP }, 448c2ecf20Sopenharmony_ci { OV9640_COM6, OV9640_COM6_OPT_BLC | OV9640_COM6_ADBLC_BIAS | 458c2ecf20Sopenharmony_ci OV9640_COM6_FMT_RST | OV9640_COM6_ADBLC_OPTEN }, 468c2ecf20Sopenharmony_ci { OV9640_PSHFT, OV9640_PSHFT_VAL(0x01) }, 478c2ecf20Sopenharmony_ci { OV9640_ACOM, OV9640_ACOM_2X_ANALOG | OV9640_ACOM_RSVD }, 488c2ecf20Sopenharmony_ci { OV9640_TSLB, OV9640_TSLB_YUYV_UYVY }, 498c2ecf20Sopenharmony_ci { OV9640_COM16, OV9640_COM16_RB_AVG }, 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci /* Gamma curve P */ 528c2ecf20Sopenharmony_ci { 0x6c, 0x40 }, { 0x6d, 0x30 }, { 0x6e, 0x4b }, { 0x6f, 0x60 }, 538c2ecf20Sopenharmony_ci { 0x70, 0x70 }, { 0x71, 0x70 }, { 0x72, 0x70 }, { 0x73, 0x70 }, 548c2ecf20Sopenharmony_ci { 0x74, 0x60 }, { 0x75, 0x60 }, { 0x76, 0x50 }, { 0x77, 0x48 }, 558c2ecf20Sopenharmony_ci { 0x78, 0x3a }, { 0x79, 0x2e }, { 0x7a, 0x28 }, { 0x7b, 0x22 }, 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci /* Gamma curve T */ 588c2ecf20Sopenharmony_ci { 0x7c, 0x04 }, { 0x7d, 0x07 }, { 0x7e, 0x10 }, { 0x7f, 0x28 }, 598c2ecf20Sopenharmony_ci { 0x80, 0x36 }, { 0x81, 0x44 }, { 0x82, 0x52 }, { 0x83, 0x60 }, 608c2ecf20Sopenharmony_ci { 0x84, 0x6c }, { 0x85, 0x78 }, { 0x86, 0x8c }, { 0x87, 0x9e }, 618c2ecf20Sopenharmony_ci { 0x88, 0xbb }, { 0x89, 0xd2 }, { 0x8a, 0xe6 }, 628c2ecf20Sopenharmony_ci}; 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci/* Configurations 658c2ecf20Sopenharmony_ci * NOTE: for YUV, alter the following registers: 668c2ecf20Sopenharmony_ci * COM12 |= OV9640_COM12_YUV_AVG 678c2ecf20Sopenharmony_ci * 688c2ecf20Sopenharmony_ci * for RGB, alter the following registers: 698c2ecf20Sopenharmony_ci * COM7 |= OV9640_COM7_RGB 708c2ecf20Sopenharmony_ci * COM13 |= OV9640_COM13_RGB_AVG 718c2ecf20Sopenharmony_ci * COM15 |= proper RGB color encoding mode 728c2ecf20Sopenharmony_ci */ 738c2ecf20Sopenharmony_cistatic const struct ov9640_reg ov9640_regs_qqcif[] = { 748c2ecf20Sopenharmony_ci { OV9640_CLKRC, OV9640_CLKRC_DPLL_EN | OV9640_CLKRC_DIV(0x0f) }, 758c2ecf20Sopenharmony_ci { OV9640_COM1, OV9640_COM1_QQFMT | OV9640_COM1_HREF_2SKIP }, 768c2ecf20Sopenharmony_ci { OV9640_COM4, OV9640_COM4_QQ_VP | OV9640_COM4_RSVD }, 778c2ecf20Sopenharmony_ci { OV9640_COM7, OV9640_COM7_QCIF }, 788c2ecf20Sopenharmony_ci { OV9640_COM12, OV9640_COM12_RSVD }, 798c2ecf20Sopenharmony_ci { OV9640_COM13, OV9640_COM13_GAMMA_RAW | OV9640_COM13_MATRIX_EN }, 808c2ecf20Sopenharmony_ci { OV9640_COM15, OV9640_COM15_OR_10F0 }, 818c2ecf20Sopenharmony_ci}; 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_cistatic const struct ov9640_reg ov9640_regs_qqvga[] = { 848c2ecf20Sopenharmony_ci { OV9640_CLKRC, OV9640_CLKRC_DPLL_EN | OV9640_CLKRC_DIV(0x07) }, 858c2ecf20Sopenharmony_ci { OV9640_COM1, OV9640_COM1_QQFMT | OV9640_COM1_HREF_2SKIP }, 868c2ecf20Sopenharmony_ci { OV9640_COM4, OV9640_COM4_QQ_VP | OV9640_COM4_RSVD }, 878c2ecf20Sopenharmony_ci { OV9640_COM7, OV9640_COM7_QVGA }, 888c2ecf20Sopenharmony_ci { OV9640_COM12, OV9640_COM12_RSVD }, 898c2ecf20Sopenharmony_ci { OV9640_COM13, OV9640_COM13_GAMMA_RAW | OV9640_COM13_MATRIX_EN }, 908c2ecf20Sopenharmony_ci { OV9640_COM15, OV9640_COM15_OR_10F0 }, 918c2ecf20Sopenharmony_ci}; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_cistatic const struct ov9640_reg ov9640_regs_qcif[] = { 948c2ecf20Sopenharmony_ci { OV9640_CLKRC, OV9640_CLKRC_DPLL_EN | OV9640_CLKRC_DIV(0x07) }, 958c2ecf20Sopenharmony_ci { OV9640_COM4, OV9640_COM4_QQ_VP | OV9640_COM4_RSVD }, 968c2ecf20Sopenharmony_ci { OV9640_COM7, OV9640_COM7_QCIF }, 978c2ecf20Sopenharmony_ci { OV9640_COM12, OV9640_COM12_RSVD }, 988c2ecf20Sopenharmony_ci { OV9640_COM13, OV9640_COM13_GAMMA_RAW | OV9640_COM13_MATRIX_EN }, 998c2ecf20Sopenharmony_ci { OV9640_COM15, OV9640_COM15_OR_10F0 }, 1008c2ecf20Sopenharmony_ci}; 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_cistatic const struct ov9640_reg ov9640_regs_qvga[] = { 1038c2ecf20Sopenharmony_ci { OV9640_CLKRC, OV9640_CLKRC_DPLL_EN | OV9640_CLKRC_DIV(0x03) }, 1048c2ecf20Sopenharmony_ci { OV9640_COM4, OV9640_COM4_QQ_VP | OV9640_COM4_RSVD }, 1058c2ecf20Sopenharmony_ci { OV9640_COM7, OV9640_COM7_QVGA }, 1068c2ecf20Sopenharmony_ci { OV9640_COM12, OV9640_COM12_RSVD }, 1078c2ecf20Sopenharmony_ci { OV9640_COM13, OV9640_COM13_GAMMA_RAW | OV9640_COM13_MATRIX_EN }, 1088c2ecf20Sopenharmony_ci { OV9640_COM15, OV9640_COM15_OR_10F0 }, 1098c2ecf20Sopenharmony_ci}; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_cistatic const struct ov9640_reg ov9640_regs_cif[] = { 1128c2ecf20Sopenharmony_ci { OV9640_CLKRC, OV9640_CLKRC_DPLL_EN | OV9640_CLKRC_DIV(0x03) }, 1138c2ecf20Sopenharmony_ci { OV9640_COM3, OV9640_COM3_VP }, 1148c2ecf20Sopenharmony_ci { OV9640_COM7, OV9640_COM7_CIF }, 1158c2ecf20Sopenharmony_ci { OV9640_COM12, OV9640_COM12_RSVD }, 1168c2ecf20Sopenharmony_ci { OV9640_COM13, OV9640_COM13_GAMMA_RAW | OV9640_COM13_MATRIX_EN }, 1178c2ecf20Sopenharmony_ci { OV9640_COM15, OV9640_COM15_OR_10F0 }, 1188c2ecf20Sopenharmony_ci}; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_cistatic const struct ov9640_reg ov9640_regs_vga[] = { 1218c2ecf20Sopenharmony_ci { OV9640_CLKRC, OV9640_CLKRC_DPLL_EN | OV9640_CLKRC_DIV(0x01) }, 1228c2ecf20Sopenharmony_ci { OV9640_COM3, OV9640_COM3_VP }, 1238c2ecf20Sopenharmony_ci { OV9640_COM7, OV9640_COM7_VGA }, 1248c2ecf20Sopenharmony_ci { OV9640_COM12, OV9640_COM12_RSVD }, 1258c2ecf20Sopenharmony_ci { OV9640_COM13, OV9640_COM13_GAMMA_RAW | OV9640_COM13_MATRIX_EN }, 1268c2ecf20Sopenharmony_ci { OV9640_COM15, OV9640_COM15_OR_10F0 }, 1278c2ecf20Sopenharmony_ci}; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_cistatic const struct ov9640_reg ov9640_regs_sxga[] = { 1308c2ecf20Sopenharmony_ci { OV9640_CLKRC, OV9640_CLKRC_DPLL_EN | OV9640_CLKRC_DIV(0x01) }, 1318c2ecf20Sopenharmony_ci { OV9640_COM3, OV9640_COM3_VP }, 1328c2ecf20Sopenharmony_ci { OV9640_COM7, 0 }, 1338c2ecf20Sopenharmony_ci { OV9640_COM12, OV9640_COM12_RSVD }, 1348c2ecf20Sopenharmony_ci { OV9640_COM13, OV9640_COM13_GAMMA_RAW | OV9640_COM13_MATRIX_EN }, 1358c2ecf20Sopenharmony_ci { OV9640_COM15, OV9640_COM15_OR_10F0 }, 1368c2ecf20Sopenharmony_ci}; 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_cistatic const struct ov9640_reg ov9640_regs_yuv[] = { 1398c2ecf20Sopenharmony_ci { OV9640_MTX1, 0x58 }, 1408c2ecf20Sopenharmony_ci { OV9640_MTX2, 0x48 }, 1418c2ecf20Sopenharmony_ci { OV9640_MTX3, 0x10 }, 1428c2ecf20Sopenharmony_ci { OV9640_MTX4, 0x28 }, 1438c2ecf20Sopenharmony_ci { OV9640_MTX5, 0x48 }, 1448c2ecf20Sopenharmony_ci { OV9640_MTX6, 0x70 }, 1458c2ecf20Sopenharmony_ci { OV9640_MTX7, 0x40 }, 1468c2ecf20Sopenharmony_ci { OV9640_MTX8, 0x40 }, 1478c2ecf20Sopenharmony_ci { OV9640_MTX9, 0x40 }, 1488c2ecf20Sopenharmony_ci { OV9640_MTXS, 0x0f }, 1498c2ecf20Sopenharmony_ci}; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_cistatic const struct ov9640_reg ov9640_regs_rgb[] = { 1528c2ecf20Sopenharmony_ci { OV9640_MTX1, 0x71 }, 1538c2ecf20Sopenharmony_ci { OV9640_MTX2, 0x3e }, 1548c2ecf20Sopenharmony_ci { OV9640_MTX3, 0x0c }, 1558c2ecf20Sopenharmony_ci { OV9640_MTX4, 0x33 }, 1568c2ecf20Sopenharmony_ci { OV9640_MTX5, 0x72 }, 1578c2ecf20Sopenharmony_ci { OV9640_MTX6, 0x00 }, 1588c2ecf20Sopenharmony_ci { OV9640_MTX7, 0x2b }, 1598c2ecf20Sopenharmony_ci { OV9640_MTX8, 0x66 }, 1608c2ecf20Sopenharmony_ci { OV9640_MTX9, 0xd2 }, 1618c2ecf20Sopenharmony_ci { OV9640_MTXS, 0x65 }, 1628c2ecf20Sopenharmony_ci}; 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_cistatic const u32 ov9640_codes[] = { 1658c2ecf20Sopenharmony_ci MEDIA_BUS_FMT_UYVY8_2X8, 1668c2ecf20Sopenharmony_ci MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE, 1678c2ecf20Sopenharmony_ci MEDIA_BUS_FMT_RGB565_2X8_LE, 1688c2ecf20Sopenharmony_ci}; 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci/* read a register */ 1718c2ecf20Sopenharmony_cistatic int ov9640_reg_read(struct i2c_client *client, u8 reg, u8 *val) 1728c2ecf20Sopenharmony_ci{ 1738c2ecf20Sopenharmony_ci int ret; 1748c2ecf20Sopenharmony_ci u8 data = reg; 1758c2ecf20Sopenharmony_ci struct i2c_msg msg = { 1768c2ecf20Sopenharmony_ci .addr = client->addr, 1778c2ecf20Sopenharmony_ci .flags = 0, 1788c2ecf20Sopenharmony_ci .len = 1, 1798c2ecf20Sopenharmony_ci .buf = &data, 1808c2ecf20Sopenharmony_ci }; 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci ret = i2c_transfer(client->adapter, &msg, 1); 1838c2ecf20Sopenharmony_ci if (ret < 0) 1848c2ecf20Sopenharmony_ci goto err; 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci msg.flags = I2C_M_RD; 1878c2ecf20Sopenharmony_ci ret = i2c_transfer(client->adapter, &msg, 1); 1888c2ecf20Sopenharmony_ci if (ret < 0) 1898c2ecf20Sopenharmony_ci goto err; 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci *val = data; 1928c2ecf20Sopenharmony_ci return 0; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_cierr: 1958c2ecf20Sopenharmony_ci dev_err(&client->dev, "Failed reading register 0x%02x!\n", reg); 1968c2ecf20Sopenharmony_ci return ret; 1978c2ecf20Sopenharmony_ci} 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci/* write a register */ 2008c2ecf20Sopenharmony_cistatic int ov9640_reg_write(struct i2c_client *client, u8 reg, u8 val) 2018c2ecf20Sopenharmony_ci{ 2028c2ecf20Sopenharmony_ci int ret; 2038c2ecf20Sopenharmony_ci u8 _val; 2048c2ecf20Sopenharmony_ci unsigned char data[2] = { reg, val }; 2058c2ecf20Sopenharmony_ci struct i2c_msg msg = { 2068c2ecf20Sopenharmony_ci .addr = client->addr, 2078c2ecf20Sopenharmony_ci .flags = 0, 2088c2ecf20Sopenharmony_ci .len = 2, 2098c2ecf20Sopenharmony_ci .buf = data, 2108c2ecf20Sopenharmony_ci }; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci ret = i2c_transfer(client->adapter, &msg, 1); 2138c2ecf20Sopenharmony_ci if (ret < 0) { 2148c2ecf20Sopenharmony_ci dev_err(&client->dev, "Failed writing register 0x%02x!\n", reg); 2158c2ecf20Sopenharmony_ci return ret; 2168c2ecf20Sopenharmony_ci } 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci /* we have to read the register back ... no idea why, maybe HW bug */ 2198c2ecf20Sopenharmony_ci ret = ov9640_reg_read(client, reg, &_val); 2208c2ecf20Sopenharmony_ci if (ret) 2218c2ecf20Sopenharmony_ci dev_err(&client->dev, 2228c2ecf20Sopenharmony_ci "Failed reading back register 0x%02x!\n", reg); 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci return 0; 2258c2ecf20Sopenharmony_ci} 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci/* Read a register, alter its bits, write it back */ 2298c2ecf20Sopenharmony_cistatic int ov9640_reg_rmw(struct i2c_client *client, u8 reg, u8 set, u8 unset) 2308c2ecf20Sopenharmony_ci{ 2318c2ecf20Sopenharmony_ci u8 val; 2328c2ecf20Sopenharmony_ci int ret; 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci ret = ov9640_reg_read(client, reg, &val); 2358c2ecf20Sopenharmony_ci if (ret) { 2368c2ecf20Sopenharmony_ci dev_err(&client->dev, 2378c2ecf20Sopenharmony_ci "[Read]-Modify-Write of register %02x failed!\n", reg); 2388c2ecf20Sopenharmony_ci return ret; 2398c2ecf20Sopenharmony_ci } 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci val |= set; 2428c2ecf20Sopenharmony_ci val &= ~unset; 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci ret = ov9640_reg_write(client, reg, val); 2458c2ecf20Sopenharmony_ci if (ret) 2468c2ecf20Sopenharmony_ci dev_err(&client->dev, 2478c2ecf20Sopenharmony_ci "Read-Modify-[Write] of register %02x failed!\n", reg); 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci return ret; 2508c2ecf20Sopenharmony_ci} 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci/* Soft reset the camera. This has nothing to do with the RESET pin! */ 2538c2ecf20Sopenharmony_cistatic int ov9640_reset(struct i2c_client *client) 2548c2ecf20Sopenharmony_ci{ 2558c2ecf20Sopenharmony_ci int ret; 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci ret = ov9640_reg_write(client, OV9640_COM7, OV9640_COM7_SCCB_RESET); 2588c2ecf20Sopenharmony_ci if (ret) 2598c2ecf20Sopenharmony_ci dev_err(&client->dev, 2608c2ecf20Sopenharmony_ci "An error occurred while entering soft reset!\n"); 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci return ret; 2638c2ecf20Sopenharmony_ci} 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci/* Start/Stop streaming from the device */ 2668c2ecf20Sopenharmony_cistatic int ov9640_s_stream(struct v4l2_subdev *sd, int enable) 2678c2ecf20Sopenharmony_ci{ 2688c2ecf20Sopenharmony_ci return 0; 2698c2ecf20Sopenharmony_ci} 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci/* Set status of additional camera capabilities */ 2728c2ecf20Sopenharmony_cistatic int ov9640_s_ctrl(struct v4l2_ctrl *ctrl) 2738c2ecf20Sopenharmony_ci{ 2748c2ecf20Sopenharmony_ci struct ov9640_priv *priv = container_of(ctrl->handler, 2758c2ecf20Sopenharmony_ci struct ov9640_priv, hdl); 2768c2ecf20Sopenharmony_ci struct i2c_client *client = v4l2_get_subdevdata(&priv->subdev); 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci switch (ctrl->id) { 2798c2ecf20Sopenharmony_ci case V4L2_CID_VFLIP: 2808c2ecf20Sopenharmony_ci if (ctrl->val) 2818c2ecf20Sopenharmony_ci return ov9640_reg_rmw(client, OV9640_MVFP, 2828c2ecf20Sopenharmony_ci OV9640_MVFP_V, 0); 2838c2ecf20Sopenharmony_ci return ov9640_reg_rmw(client, OV9640_MVFP, 0, OV9640_MVFP_V); 2848c2ecf20Sopenharmony_ci case V4L2_CID_HFLIP: 2858c2ecf20Sopenharmony_ci if (ctrl->val) 2868c2ecf20Sopenharmony_ci return ov9640_reg_rmw(client, OV9640_MVFP, 2878c2ecf20Sopenharmony_ci OV9640_MVFP_H, 0); 2888c2ecf20Sopenharmony_ci return ov9640_reg_rmw(client, OV9640_MVFP, 0, OV9640_MVFP_H); 2898c2ecf20Sopenharmony_ci } 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci return -EINVAL; 2928c2ecf20Sopenharmony_ci} 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci#ifdef CONFIG_VIDEO_ADV_DEBUG 2958c2ecf20Sopenharmony_cistatic int ov9640_get_register(struct v4l2_subdev *sd, 2968c2ecf20Sopenharmony_ci struct v4l2_dbg_register *reg) 2978c2ecf20Sopenharmony_ci{ 2988c2ecf20Sopenharmony_ci struct i2c_client *client = v4l2_get_subdevdata(sd); 2998c2ecf20Sopenharmony_ci int ret; 3008c2ecf20Sopenharmony_ci u8 val; 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci if (reg->reg & ~0xff) 3038c2ecf20Sopenharmony_ci return -EINVAL; 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci reg->size = 1; 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci ret = ov9640_reg_read(client, reg->reg, &val); 3088c2ecf20Sopenharmony_ci if (ret) 3098c2ecf20Sopenharmony_ci return ret; 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci reg->val = (__u64)val; 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci return 0; 3148c2ecf20Sopenharmony_ci} 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_cistatic int ov9640_set_register(struct v4l2_subdev *sd, 3178c2ecf20Sopenharmony_ci const struct v4l2_dbg_register *reg) 3188c2ecf20Sopenharmony_ci{ 3198c2ecf20Sopenharmony_ci struct i2c_client *client = v4l2_get_subdevdata(sd); 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci if (reg->reg & ~0xff || reg->val & ~0xff) 3228c2ecf20Sopenharmony_ci return -EINVAL; 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci return ov9640_reg_write(client, reg->reg, reg->val); 3258c2ecf20Sopenharmony_ci} 3268c2ecf20Sopenharmony_ci#endif 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_cistatic int ov9640_s_power(struct v4l2_subdev *sd, int on) 3298c2ecf20Sopenharmony_ci{ 3308c2ecf20Sopenharmony_ci struct ov9640_priv *priv = to_ov9640_sensor(sd); 3318c2ecf20Sopenharmony_ci int ret = 0; 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci if (on) { 3348c2ecf20Sopenharmony_ci gpiod_set_value(priv->gpio_power, 1); 3358c2ecf20Sopenharmony_ci usleep_range(1000, 2000); 3368c2ecf20Sopenharmony_ci ret = v4l2_clk_enable(priv->clk); 3378c2ecf20Sopenharmony_ci usleep_range(1000, 2000); 3388c2ecf20Sopenharmony_ci gpiod_set_value(priv->gpio_reset, 0); 3398c2ecf20Sopenharmony_ci } else { 3408c2ecf20Sopenharmony_ci gpiod_set_value(priv->gpio_reset, 1); 3418c2ecf20Sopenharmony_ci usleep_range(1000, 2000); 3428c2ecf20Sopenharmony_ci v4l2_clk_disable(priv->clk); 3438c2ecf20Sopenharmony_ci usleep_range(1000, 2000); 3448c2ecf20Sopenharmony_ci gpiod_set_value(priv->gpio_power, 0); 3458c2ecf20Sopenharmony_ci } 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci return ret; 3488c2ecf20Sopenharmony_ci} 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci/* select nearest higher resolution for capture */ 3518c2ecf20Sopenharmony_cistatic void ov9640_res_roundup(u32 *width, u32 *height) 3528c2ecf20Sopenharmony_ci{ 3538c2ecf20Sopenharmony_ci unsigned int i; 3548c2ecf20Sopenharmony_ci enum { QQCIF, QQVGA, QCIF, QVGA, CIF, VGA, SXGA }; 3558c2ecf20Sopenharmony_ci static const u32 res_x[] = { 88, 160, 176, 320, 352, 640, 1280 }; 3568c2ecf20Sopenharmony_ci static const u32 res_y[] = { 72, 120, 144, 240, 288, 480, 960 }; 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(res_x); i++) { 3598c2ecf20Sopenharmony_ci if (res_x[i] >= *width && res_y[i] >= *height) { 3608c2ecf20Sopenharmony_ci *width = res_x[i]; 3618c2ecf20Sopenharmony_ci *height = res_y[i]; 3628c2ecf20Sopenharmony_ci return; 3638c2ecf20Sopenharmony_ci } 3648c2ecf20Sopenharmony_ci } 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci *width = res_x[SXGA]; 3678c2ecf20Sopenharmony_ci *height = res_y[SXGA]; 3688c2ecf20Sopenharmony_ci} 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci/* Prepare necessary register changes depending on color encoding */ 3718c2ecf20Sopenharmony_cistatic void ov9640_alter_regs(u32 code, 3728c2ecf20Sopenharmony_ci struct ov9640_reg_alt *alt) 3738c2ecf20Sopenharmony_ci{ 3748c2ecf20Sopenharmony_ci switch (code) { 3758c2ecf20Sopenharmony_ci default: 3768c2ecf20Sopenharmony_ci case MEDIA_BUS_FMT_UYVY8_2X8: 3778c2ecf20Sopenharmony_ci alt->com12 = OV9640_COM12_YUV_AVG; 3788c2ecf20Sopenharmony_ci alt->com13 = OV9640_COM13_Y_DELAY_EN | 3798c2ecf20Sopenharmony_ci OV9640_COM13_YUV_DLY(0x01); 3808c2ecf20Sopenharmony_ci break; 3818c2ecf20Sopenharmony_ci case MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE: 3828c2ecf20Sopenharmony_ci alt->com7 = OV9640_COM7_RGB; 3838c2ecf20Sopenharmony_ci alt->com13 = OV9640_COM13_RGB_AVG; 3848c2ecf20Sopenharmony_ci alt->com15 = OV9640_COM15_RGB_555; 3858c2ecf20Sopenharmony_ci break; 3868c2ecf20Sopenharmony_ci case MEDIA_BUS_FMT_RGB565_2X8_LE: 3878c2ecf20Sopenharmony_ci alt->com7 = OV9640_COM7_RGB; 3888c2ecf20Sopenharmony_ci alt->com13 = OV9640_COM13_RGB_AVG; 3898c2ecf20Sopenharmony_ci alt->com15 = OV9640_COM15_RGB_565; 3908c2ecf20Sopenharmony_ci break; 3918c2ecf20Sopenharmony_ci } 3928c2ecf20Sopenharmony_ci} 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci/* Setup registers according to resolution and color encoding */ 3958c2ecf20Sopenharmony_cistatic int ov9640_write_regs(struct i2c_client *client, u32 width, 3968c2ecf20Sopenharmony_ci u32 code, struct ov9640_reg_alt *alts) 3978c2ecf20Sopenharmony_ci{ 3988c2ecf20Sopenharmony_ci const struct ov9640_reg *ov9640_regs, *matrix_regs; 3998c2ecf20Sopenharmony_ci unsigned int ov9640_regs_len, matrix_regs_len; 4008c2ecf20Sopenharmony_ci unsigned int i; 4018c2ecf20Sopenharmony_ci int ret; 4028c2ecf20Sopenharmony_ci u8 val; 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci /* select register configuration for given resolution */ 4058c2ecf20Sopenharmony_ci switch (width) { 4068c2ecf20Sopenharmony_ci case W_QQCIF: 4078c2ecf20Sopenharmony_ci ov9640_regs = ov9640_regs_qqcif; 4088c2ecf20Sopenharmony_ci ov9640_regs_len = ARRAY_SIZE(ov9640_regs_qqcif); 4098c2ecf20Sopenharmony_ci break; 4108c2ecf20Sopenharmony_ci case W_QQVGA: 4118c2ecf20Sopenharmony_ci ov9640_regs = ov9640_regs_qqvga; 4128c2ecf20Sopenharmony_ci ov9640_regs_len = ARRAY_SIZE(ov9640_regs_qqvga); 4138c2ecf20Sopenharmony_ci break; 4148c2ecf20Sopenharmony_ci case W_QCIF: 4158c2ecf20Sopenharmony_ci ov9640_regs = ov9640_regs_qcif; 4168c2ecf20Sopenharmony_ci ov9640_regs_len = ARRAY_SIZE(ov9640_regs_qcif); 4178c2ecf20Sopenharmony_ci break; 4188c2ecf20Sopenharmony_ci case W_QVGA: 4198c2ecf20Sopenharmony_ci ov9640_regs = ov9640_regs_qvga; 4208c2ecf20Sopenharmony_ci ov9640_regs_len = ARRAY_SIZE(ov9640_regs_qvga); 4218c2ecf20Sopenharmony_ci break; 4228c2ecf20Sopenharmony_ci case W_CIF: 4238c2ecf20Sopenharmony_ci ov9640_regs = ov9640_regs_cif; 4248c2ecf20Sopenharmony_ci ov9640_regs_len = ARRAY_SIZE(ov9640_regs_cif); 4258c2ecf20Sopenharmony_ci break; 4268c2ecf20Sopenharmony_ci case W_VGA: 4278c2ecf20Sopenharmony_ci ov9640_regs = ov9640_regs_vga; 4288c2ecf20Sopenharmony_ci ov9640_regs_len = ARRAY_SIZE(ov9640_regs_vga); 4298c2ecf20Sopenharmony_ci break; 4308c2ecf20Sopenharmony_ci case W_SXGA: 4318c2ecf20Sopenharmony_ci ov9640_regs = ov9640_regs_sxga; 4328c2ecf20Sopenharmony_ci ov9640_regs_len = ARRAY_SIZE(ov9640_regs_sxga); 4338c2ecf20Sopenharmony_ci break; 4348c2ecf20Sopenharmony_ci default: 4358c2ecf20Sopenharmony_ci dev_err(&client->dev, "Failed to select resolution!\n"); 4368c2ecf20Sopenharmony_ci return -EINVAL; 4378c2ecf20Sopenharmony_ci } 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci /* select color matrix configuration for given color encoding */ 4408c2ecf20Sopenharmony_ci if (code == MEDIA_BUS_FMT_UYVY8_2X8) { 4418c2ecf20Sopenharmony_ci matrix_regs = ov9640_regs_yuv; 4428c2ecf20Sopenharmony_ci matrix_regs_len = ARRAY_SIZE(ov9640_regs_yuv); 4438c2ecf20Sopenharmony_ci } else { 4448c2ecf20Sopenharmony_ci matrix_regs = ov9640_regs_rgb; 4458c2ecf20Sopenharmony_ci matrix_regs_len = ARRAY_SIZE(ov9640_regs_rgb); 4468c2ecf20Sopenharmony_ci } 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci /* write register settings into the module */ 4498c2ecf20Sopenharmony_ci for (i = 0; i < ov9640_regs_len; i++) { 4508c2ecf20Sopenharmony_ci val = ov9640_regs[i].val; 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci switch (ov9640_regs[i].reg) { 4538c2ecf20Sopenharmony_ci case OV9640_COM7: 4548c2ecf20Sopenharmony_ci val |= alts->com7; 4558c2ecf20Sopenharmony_ci break; 4568c2ecf20Sopenharmony_ci case OV9640_COM12: 4578c2ecf20Sopenharmony_ci val |= alts->com12; 4588c2ecf20Sopenharmony_ci break; 4598c2ecf20Sopenharmony_ci case OV9640_COM13: 4608c2ecf20Sopenharmony_ci val |= alts->com13; 4618c2ecf20Sopenharmony_ci break; 4628c2ecf20Sopenharmony_ci case OV9640_COM15: 4638c2ecf20Sopenharmony_ci val |= alts->com15; 4648c2ecf20Sopenharmony_ci break; 4658c2ecf20Sopenharmony_ci } 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci ret = ov9640_reg_write(client, ov9640_regs[i].reg, val); 4688c2ecf20Sopenharmony_ci if (ret) 4698c2ecf20Sopenharmony_ci return ret; 4708c2ecf20Sopenharmony_ci } 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_ci /* write color matrix configuration into the module */ 4738c2ecf20Sopenharmony_ci for (i = 0; i < matrix_regs_len; i++) { 4748c2ecf20Sopenharmony_ci ret = ov9640_reg_write(client, matrix_regs[i].reg, 4758c2ecf20Sopenharmony_ci matrix_regs[i].val); 4768c2ecf20Sopenharmony_ci if (ret) 4778c2ecf20Sopenharmony_ci return ret; 4788c2ecf20Sopenharmony_ci } 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci return 0; 4818c2ecf20Sopenharmony_ci} 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci/* program default register values */ 4848c2ecf20Sopenharmony_cistatic int ov9640_prog_dflt(struct i2c_client *client) 4858c2ecf20Sopenharmony_ci{ 4868c2ecf20Sopenharmony_ci unsigned int i; 4878c2ecf20Sopenharmony_ci int ret; 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(ov9640_regs_dflt); i++) { 4908c2ecf20Sopenharmony_ci ret = ov9640_reg_write(client, ov9640_regs_dflt[i].reg, 4918c2ecf20Sopenharmony_ci ov9640_regs_dflt[i].val); 4928c2ecf20Sopenharmony_ci if (ret) 4938c2ecf20Sopenharmony_ci return ret; 4948c2ecf20Sopenharmony_ci } 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci /* wait for the changes to actually happen, 140ms are not enough yet */ 4978c2ecf20Sopenharmony_ci msleep(150); 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci return 0; 5008c2ecf20Sopenharmony_ci} 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci/* set the format we will capture in */ 5038c2ecf20Sopenharmony_cistatic int ov9640_s_fmt(struct v4l2_subdev *sd, 5048c2ecf20Sopenharmony_ci struct v4l2_mbus_framefmt *mf) 5058c2ecf20Sopenharmony_ci{ 5068c2ecf20Sopenharmony_ci struct i2c_client *client = v4l2_get_subdevdata(sd); 5078c2ecf20Sopenharmony_ci struct ov9640_reg_alt alts = {0}; 5088c2ecf20Sopenharmony_ci int ret; 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci ov9640_alter_regs(mf->code, &alts); 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci ov9640_reset(client); 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_ci ret = ov9640_prog_dflt(client); 5158c2ecf20Sopenharmony_ci if (ret) 5168c2ecf20Sopenharmony_ci return ret; 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci return ov9640_write_regs(client, mf->width, mf->code, &alts); 5198c2ecf20Sopenharmony_ci} 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_cistatic int ov9640_set_fmt(struct v4l2_subdev *sd, 5228c2ecf20Sopenharmony_ci struct v4l2_subdev_pad_config *cfg, 5238c2ecf20Sopenharmony_ci struct v4l2_subdev_format *format) 5248c2ecf20Sopenharmony_ci{ 5258c2ecf20Sopenharmony_ci struct v4l2_mbus_framefmt *mf = &format->format; 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ci if (format->pad) 5288c2ecf20Sopenharmony_ci return -EINVAL; 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ci ov9640_res_roundup(&mf->width, &mf->height); 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci mf->field = V4L2_FIELD_NONE; 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ci switch (mf->code) { 5358c2ecf20Sopenharmony_ci case MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE: 5368c2ecf20Sopenharmony_ci case MEDIA_BUS_FMT_RGB565_2X8_LE: 5378c2ecf20Sopenharmony_ci mf->colorspace = V4L2_COLORSPACE_SRGB; 5388c2ecf20Sopenharmony_ci break; 5398c2ecf20Sopenharmony_ci default: 5408c2ecf20Sopenharmony_ci mf->code = MEDIA_BUS_FMT_UYVY8_2X8; 5418c2ecf20Sopenharmony_ci fallthrough; 5428c2ecf20Sopenharmony_ci case MEDIA_BUS_FMT_UYVY8_2X8: 5438c2ecf20Sopenharmony_ci mf->colorspace = V4L2_COLORSPACE_JPEG; 5448c2ecf20Sopenharmony_ci break; 5458c2ecf20Sopenharmony_ci } 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ci if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) 5488c2ecf20Sopenharmony_ci return ov9640_s_fmt(sd, mf); 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci cfg->try_fmt = *mf; 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_ci return 0; 5538c2ecf20Sopenharmony_ci} 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_cistatic int ov9640_enum_mbus_code(struct v4l2_subdev *sd, 5568c2ecf20Sopenharmony_ci struct v4l2_subdev_pad_config *cfg, 5578c2ecf20Sopenharmony_ci struct v4l2_subdev_mbus_code_enum *code) 5588c2ecf20Sopenharmony_ci{ 5598c2ecf20Sopenharmony_ci if (code->pad || code->index >= ARRAY_SIZE(ov9640_codes)) 5608c2ecf20Sopenharmony_ci return -EINVAL; 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_ci code->code = ov9640_codes[code->index]; 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ci return 0; 5658c2ecf20Sopenharmony_ci} 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_cistatic int ov9640_get_selection(struct v4l2_subdev *sd, 5688c2ecf20Sopenharmony_ci struct v4l2_subdev_pad_config *cfg, 5698c2ecf20Sopenharmony_ci struct v4l2_subdev_selection *sel) 5708c2ecf20Sopenharmony_ci{ 5718c2ecf20Sopenharmony_ci if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE) 5728c2ecf20Sopenharmony_ci return -EINVAL; 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci sel->r.left = 0; 5758c2ecf20Sopenharmony_ci sel->r.top = 0; 5768c2ecf20Sopenharmony_ci switch (sel->target) { 5778c2ecf20Sopenharmony_ci case V4L2_SEL_TGT_CROP_BOUNDS: 5788c2ecf20Sopenharmony_ci case V4L2_SEL_TGT_CROP: 5798c2ecf20Sopenharmony_ci sel->r.width = W_SXGA; 5808c2ecf20Sopenharmony_ci sel->r.height = H_SXGA; 5818c2ecf20Sopenharmony_ci return 0; 5828c2ecf20Sopenharmony_ci default: 5838c2ecf20Sopenharmony_ci return -EINVAL; 5848c2ecf20Sopenharmony_ci } 5858c2ecf20Sopenharmony_ci} 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_cistatic int ov9640_video_probe(struct i2c_client *client) 5888c2ecf20Sopenharmony_ci{ 5898c2ecf20Sopenharmony_ci struct v4l2_subdev *sd = i2c_get_clientdata(client); 5908c2ecf20Sopenharmony_ci struct ov9640_priv *priv = to_ov9640_sensor(sd); 5918c2ecf20Sopenharmony_ci u8 pid, ver, midh, midl; 5928c2ecf20Sopenharmony_ci const char *devname; 5938c2ecf20Sopenharmony_ci int ret; 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_ci ret = ov9640_s_power(&priv->subdev, 1); 5968c2ecf20Sopenharmony_ci if (ret < 0) 5978c2ecf20Sopenharmony_ci return ret; 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_ci /* 6008c2ecf20Sopenharmony_ci * check and show product ID and manufacturer ID 6018c2ecf20Sopenharmony_ci */ 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_ci ret = ov9640_reg_read(client, OV9640_PID, &pid); 6048c2ecf20Sopenharmony_ci if (!ret) 6058c2ecf20Sopenharmony_ci ret = ov9640_reg_read(client, OV9640_VER, &ver); 6068c2ecf20Sopenharmony_ci if (!ret) 6078c2ecf20Sopenharmony_ci ret = ov9640_reg_read(client, OV9640_MIDH, &midh); 6088c2ecf20Sopenharmony_ci if (!ret) 6098c2ecf20Sopenharmony_ci ret = ov9640_reg_read(client, OV9640_MIDL, &midl); 6108c2ecf20Sopenharmony_ci if (ret) 6118c2ecf20Sopenharmony_ci goto done; 6128c2ecf20Sopenharmony_ci 6138c2ecf20Sopenharmony_ci switch (VERSION(pid, ver)) { 6148c2ecf20Sopenharmony_ci case OV9640_V2: 6158c2ecf20Sopenharmony_ci devname = "ov9640"; 6168c2ecf20Sopenharmony_ci priv->revision = 2; 6178c2ecf20Sopenharmony_ci break; 6188c2ecf20Sopenharmony_ci case OV9640_V3: 6198c2ecf20Sopenharmony_ci devname = "ov9640"; 6208c2ecf20Sopenharmony_ci priv->revision = 3; 6218c2ecf20Sopenharmony_ci break; 6228c2ecf20Sopenharmony_ci default: 6238c2ecf20Sopenharmony_ci dev_err(&client->dev, "Product ID error %x:%x\n", pid, ver); 6248c2ecf20Sopenharmony_ci ret = -ENODEV; 6258c2ecf20Sopenharmony_ci goto done; 6268c2ecf20Sopenharmony_ci } 6278c2ecf20Sopenharmony_ci 6288c2ecf20Sopenharmony_ci dev_info(&client->dev, "%s Product ID %0x:%0x Manufacturer ID %x:%x\n", 6298c2ecf20Sopenharmony_ci devname, pid, ver, midh, midl); 6308c2ecf20Sopenharmony_ci 6318c2ecf20Sopenharmony_ci ret = v4l2_ctrl_handler_setup(&priv->hdl); 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_cidone: 6348c2ecf20Sopenharmony_ci ov9640_s_power(&priv->subdev, 0); 6358c2ecf20Sopenharmony_ci return ret; 6368c2ecf20Sopenharmony_ci} 6378c2ecf20Sopenharmony_ci 6388c2ecf20Sopenharmony_cistatic const struct v4l2_ctrl_ops ov9640_ctrl_ops = { 6398c2ecf20Sopenharmony_ci .s_ctrl = ov9640_s_ctrl, 6408c2ecf20Sopenharmony_ci}; 6418c2ecf20Sopenharmony_ci 6428c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_core_ops ov9640_core_ops = { 6438c2ecf20Sopenharmony_ci#ifdef CONFIG_VIDEO_ADV_DEBUG 6448c2ecf20Sopenharmony_ci .g_register = ov9640_get_register, 6458c2ecf20Sopenharmony_ci .s_register = ov9640_set_register, 6468c2ecf20Sopenharmony_ci#endif 6478c2ecf20Sopenharmony_ci .s_power = ov9640_s_power, 6488c2ecf20Sopenharmony_ci}; 6498c2ecf20Sopenharmony_ci 6508c2ecf20Sopenharmony_ci/* Request bus settings on camera side */ 6518c2ecf20Sopenharmony_cistatic int ov9640_get_mbus_config(struct v4l2_subdev *sd, 6528c2ecf20Sopenharmony_ci unsigned int pad, 6538c2ecf20Sopenharmony_ci struct v4l2_mbus_config *cfg) 6548c2ecf20Sopenharmony_ci{ 6558c2ecf20Sopenharmony_ci cfg->flags = V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_MASTER | 6568c2ecf20Sopenharmony_ci V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_HIGH | 6578c2ecf20Sopenharmony_ci V4L2_MBUS_DATA_ACTIVE_HIGH; 6588c2ecf20Sopenharmony_ci cfg->type = V4L2_MBUS_PARALLEL; 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_ci return 0; 6618c2ecf20Sopenharmony_ci} 6628c2ecf20Sopenharmony_ci 6638c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_video_ops ov9640_video_ops = { 6648c2ecf20Sopenharmony_ci .s_stream = ov9640_s_stream, 6658c2ecf20Sopenharmony_ci}; 6668c2ecf20Sopenharmony_ci 6678c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_pad_ops ov9640_pad_ops = { 6688c2ecf20Sopenharmony_ci .enum_mbus_code = ov9640_enum_mbus_code, 6698c2ecf20Sopenharmony_ci .get_selection = ov9640_get_selection, 6708c2ecf20Sopenharmony_ci .set_fmt = ov9640_set_fmt, 6718c2ecf20Sopenharmony_ci .get_mbus_config = ov9640_get_mbus_config, 6728c2ecf20Sopenharmony_ci}; 6738c2ecf20Sopenharmony_ci 6748c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_ops ov9640_subdev_ops = { 6758c2ecf20Sopenharmony_ci .core = &ov9640_core_ops, 6768c2ecf20Sopenharmony_ci .video = &ov9640_video_ops, 6778c2ecf20Sopenharmony_ci .pad = &ov9640_pad_ops, 6788c2ecf20Sopenharmony_ci}; 6798c2ecf20Sopenharmony_ci 6808c2ecf20Sopenharmony_ci/* 6818c2ecf20Sopenharmony_ci * i2c_driver function 6828c2ecf20Sopenharmony_ci */ 6838c2ecf20Sopenharmony_cistatic int ov9640_probe(struct i2c_client *client, 6848c2ecf20Sopenharmony_ci const struct i2c_device_id *did) 6858c2ecf20Sopenharmony_ci{ 6868c2ecf20Sopenharmony_ci struct ov9640_priv *priv; 6878c2ecf20Sopenharmony_ci int ret; 6888c2ecf20Sopenharmony_ci 6898c2ecf20Sopenharmony_ci priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL); 6908c2ecf20Sopenharmony_ci if (!priv) 6918c2ecf20Sopenharmony_ci return -ENOMEM; 6928c2ecf20Sopenharmony_ci 6938c2ecf20Sopenharmony_ci priv->gpio_power = devm_gpiod_get(&client->dev, "Camera power", 6948c2ecf20Sopenharmony_ci GPIOD_OUT_LOW); 6958c2ecf20Sopenharmony_ci if (IS_ERR(priv->gpio_power)) { 6968c2ecf20Sopenharmony_ci ret = PTR_ERR(priv->gpio_power); 6978c2ecf20Sopenharmony_ci return ret; 6988c2ecf20Sopenharmony_ci } 6998c2ecf20Sopenharmony_ci 7008c2ecf20Sopenharmony_ci priv->gpio_reset = devm_gpiod_get(&client->dev, "Camera reset", 7018c2ecf20Sopenharmony_ci GPIOD_OUT_HIGH); 7028c2ecf20Sopenharmony_ci if (IS_ERR(priv->gpio_reset)) { 7038c2ecf20Sopenharmony_ci ret = PTR_ERR(priv->gpio_reset); 7048c2ecf20Sopenharmony_ci return ret; 7058c2ecf20Sopenharmony_ci } 7068c2ecf20Sopenharmony_ci 7078c2ecf20Sopenharmony_ci v4l2_i2c_subdev_init(&priv->subdev, client, &ov9640_subdev_ops); 7088c2ecf20Sopenharmony_ci 7098c2ecf20Sopenharmony_ci v4l2_ctrl_handler_init(&priv->hdl, 2); 7108c2ecf20Sopenharmony_ci v4l2_ctrl_new_std(&priv->hdl, &ov9640_ctrl_ops, 7118c2ecf20Sopenharmony_ci V4L2_CID_VFLIP, 0, 1, 1, 0); 7128c2ecf20Sopenharmony_ci v4l2_ctrl_new_std(&priv->hdl, &ov9640_ctrl_ops, 7138c2ecf20Sopenharmony_ci V4L2_CID_HFLIP, 0, 1, 1, 0); 7148c2ecf20Sopenharmony_ci 7158c2ecf20Sopenharmony_ci if (priv->hdl.error) { 7168c2ecf20Sopenharmony_ci ret = priv->hdl.error; 7178c2ecf20Sopenharmony_ci goto ectrlinit; 7188c2ecf20Sopenharmony_ci } 7198c2ecf20Sopenharmony_ci 7208c2ecf20Sopenharmony_ci priv->subdev.ctrl_handler = &priv->hdl; 7218c2ecf20Sopenharmony_ci 7228c2ecf20Sopenharmony_ci priv->clk = v4l2_clk_get(&client->dev, "mclk"); 7238c2ecf20Sopenharmony_ci if (IS_ERR(priv->clk)) { 7248c2ecf20Sopenharmony_ci ret = PTR_ERR(priv->clk); 7258c2ecf20Sopenharmony_ci goto ectrlinit; 7268c2ecf20Sopenharmony_ci } 7278c2ecf20Sopenharmony_ci 7288c2ecf20Sopenharmony_ci ret = ov9640_video_probe(client); 7298c2ecf20Sopenharmony_ci if (ret) 7308c2ecf20Sopenharmony_ci goto eprobe; 7318c2ecf20Sopenharmony_ci 7328c2ecf20Sopenharmony_ci priv->subdev.dev = &client->dev; 7338c2ecf20Sopenharmony_ci ret = v4l2_async_register_subdev(&priv->subdev); 7348c2ecf20Sopenharmony_ci if (ret) 7358c2ecf20Sopenharmony_ci goto eprobe; 7368c2ecf20Sopenharmony_ci 7378c2ecf20Sopenharmony_ci return 0; 7388c2ecf20Sopenharmony_ci 7398c2ecf20Sopenharmony_cieprobe: 7408c2ecf20Sopenharmony_ci v4l2_clk_put(priv->clk); 7418c2ecf20Sopenharmony_ciectrlinit: 7428c2ecf20Sopenharmony_ci v4l2_ctrl_handler_free(&priv->hdl); 7438c2ecf20Sopenharmony_ci 7448c2ecf20Sopenharmony_ci return ret; 7458c2ecf20Sopenharmony_ci} 7468c2ecf20Sopenharmony_ci 7478c2ecf20Sopenharmony_cistatic int ov9640_remove(struct i2c_client *client) 7488c2ecf20Sopenharmony_ci{ 7498c2ecf20Sopenharmony_ci struct v4l2_subdev *sd = i2c_get_clientdata(client); 7508c2ecf20Sopenharmony_ci struct ov9640_priv *priv = to_ov9640_sensor(sd); 7518c2ecf20Sopenharmony_ci 7528c2ecf20Sopenharmony_ci v4l2_clk_put(priv->clk); 7538c2ecf20Sopenharmony_ci v4l2_async_unregister_subdev(&priv->subdev); 7548c2ecf20Sopenharmony_ci v4l2_ctrl_handler_free(&priv->hdl); 7558c2ecf20Sopenharmony_ci 7568c2ecf20Sopenharmony_ci return 0; 7578c2ecf20Sopenharmony_ci} 7588c2ecf20Sopenharmony_ci 7598c2ecf20Sopenharmony_cistatic const struct i2c_device_id ov9640_id[] = { 7608c2ecf20Sopenharmony_ci { "ov9640", 0 }, 7618c2ecf20Sopenharmony_ci { } 7628c2ecf20Sopenharmony_ci}; 7638c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, ov9640_id); 7648c2ecf20Sopenharmony_ci 7658c2ecf20Sopenharmony_cistatic struct i2c_driver ov9640_i2c_driver = { 7668c2ecf20Sopenharmony_ci .driver = { 7678c2ecf20Sopenharmony_ci .name = "ov9640", 7688c2ecf20Sopenharmony_ci }, 7698c2ecf20Sopenharmony_ci .probe = ov9640_probe, 7708c2ecf20Sopenharmony_ci .remove = ov9640_remove, 7718c2ecf20Sopenharmony_ci .id_table = ov9640_id, 7728c2ecf20Sopenharmony_ci}; 7738c2ecf20Sopenharmony_ci 7748c2ecf20Sopenharmony_cimodule_i2c_driver(ov9640_i2c_driver); 7758c2ecf20Sopenharmony_ci 7768c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("OmniVision OV96xx CMOS Image Sensor driver"); 7778c2ecf20Sopenharmony_ciMODULE_AUTHOR("Marek Vasut <marek.vasut@gmail.com>"); 7788c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 779