18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * V4L2 sensor driver for Aptina MT9V111 image sensor 48c2ecf20Sopenharmony_ci * Copyright (C) 2018 Jacopo Mondi <jacopo@jmondi.org> 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Based on mt9v032 driver 78c2ecf20Sopenharmony_ci * Copyright (C) 2010, Laurent Pinchart <laurent.pinchart@ideasonboard.com> 88c2ecf20Sopenharmony_ci * Copyright (C) 2008, Guennadi Liakhovetski <kernel@pengutronix.de> 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci * Based on mt9v011 driver 118c2ecf20Sopenharmony_ci * Copyright (c) 2009 Mauro Carvalho Chehab <mchehab@kernel.org> 128c2ecf20Sopenharmony_ci */ 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include <linux/clk.h> 158c2ecf20Sopenharmony_ci#include <linux/delay.h> 168c2ecf20Sopenharmony_ci#include <linux/gpio/consumer.h> 178c2ecf20Sopenharmony_ci#include <linux/i2c.h> 188c2ecf20Sopenharmony_ci#include <linux/of.h> 198c2ecf20Sopenharmony_ci#include <linux/slab.h> 208c2ecf20Sopenharmony_ci#include <linux/videodev2.h> 218c2ecf20Sopenharmony_ci#include <linux/v4l2-mediabus.h> 228c2ecf20Sopenharmony_ci#include <linux/module.h> 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#include <media/v4l2-ctrls.h> 258c2ecf20Sopenharmony_ci#include <media/v4l2-device.h> 268c2ecf20Sopenharmony_ci#include <media/v4l2-fwnode.h> 278c2ecf20Sopenharmony_ci#include <media/v4l2-image-sizes.h> 288c2ecf20Sopenharmony_ci#include <media/v4l2-subdev.h> 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci/* 318c2ecf20Sopenharmony_ci * MT9V111 is a 1/4-Inch CMOS digital image sensor with an integrated 328c2ecf20Sopenharmony_ci * Image Flow Processing (IFP) engine and a sensor core loosely based on 338c2ecf20Sopenharmony_ci * MT9V011. 348c2ecf20Sopenharmony_ci * 358c2ecf20Sopenharmony_ci * The IFP can produce several output image formats from the sensor core 368c2ecf20Sopenharmony_ci * output. This driver currently supports only YUYV format permutations. 378c2ecf20Sopenharmony_ci * 388c2ecf20Sopenharmony_ci * The driver allows manual frame rate control through s_frame_interval subdev 398c2ecf20Sopenharmony_ci * operation or V4L2_CID_V/HBLANK controls, but it is known that the 408c2ecf20Sopenharmony_ci * auto-exposure algorithm might modify the programmed frame rate. While the 418c2ecf20Sopenharmony_ci * driver initially programs the sensor with auto-exposure and 428c2ecf20Sopenharmony_ci * auto-white-balancing enabled, it is possible to disable them and more 438c2ecf20Sopenharmony_ci * precisely control the frame rate. 448c2ecf20Sopenharmony_ci * 458c2ecf20Sopenharmony_ci * While it seems possible to instruct the auto-exposure control algorithm to 468c2ecf20Sopenharmony_ci * respect a programmed frame rate when adjusting the pixel integration time, 478c2ecf20Sopenharmony_ci * registers controlling this feature are not documented in the public 488c2ecf20Sopenharmony_ci * available sensor manual used to develop this driver (09005aef80e90084, 498c2ecf20Sopenharmony_ci * MT9V111_1.fm - Rev. G 1/05 EN). 508c2ecf20Sopenharmony_ci */ 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci#define MT9V111_CHIP_ID_HIGH 0x82 538c2ecf20Sopenharmony_ci#define MT9V111_CHIP_ID_LOW 0x3a 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci#define MT9V111_R01_ADDR_SPACE 0x01 568c2ecf20Sopenharmony_ci#define MT9V111_R01_IFP 0x01 578c2ecf20Sopenharmony_ci#define MT9V111_R01_CORE 0x04 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci#define MT9V111_IFP_R06_OPMODE_CTRL 0x06 608c2ecf20Sopenharmony_ci#define MT9V111_IFP_R06_OPMODE_CTRL_AWB_EN BIT(1) 618c2ecf20Sopenharmony_ci#define MT9V111_IFP_R06_OPMODE_CTRL_AE_EN BIT(14) 628c2ecf20Sopenharmony_ci#define MT9V111_IFP_R07_IFP_RESET 0x07 638c2ecf20Sopenharmony_ci#define MT9V111_IFP_R07_IFP_RESET_MASK BIT(0) 648c2ecf20Sopenharmony_ci#define MT9V111_IFP_R08_OUTFMT_CTRL 0x08 658c2ecf20Sopenharmony_ci#define MT9V111_IFP_R08_OUTFMT_CTRL_FLICKER BIT(11) 668c2ecf20Sopenharmony_ci#define MT9V111_IFP_R08_OUTFMT_CTRL_PCLK BIT(5) 678c2ecf20Sopenharmony_ci#define MT9V111_IFP_R3A_OUTFMT_CTRL2 0x3a 688c2ecf20Sopenharmony_ci#define MT9V111_IFP_R3A_OUTFMT_CTRL2_SWAP_CBCR BIT(0) 698c2ecf20Sopenharmony_ci#define MT9V111_IFP_R3A_OUTFMT_CTRL2_SWAP_YC BIT(1) 708c2ecf20Sopenharmony_ci#define MT9V111_IFP_R3A_OUTFMT_CTRL2_SWAP_MASK GENMASK(2, 0) 718c2ecf20Sopenharmony_ci#define MT9V111_IFP_RA5_HPAN 0xa5 728c2ecf20Sopenharmony_ci#define MT9V111_IFP_RA6_HZOOM 0xa6 738c2ecf20Sopenharmony_ci#define MT9V111_IFP_RA7_HOUT 0xa7 748c2ecf20Sopenharmony_ci#define MT9V111_IFP_RA8_VPAN 0xa8 758c2ecf20Sopenharmony_ci#define MT9V111_IFP_RA9_VZOOM 0xa9 768c2ecf20Sopenharmony_ci#define MT9V111_IFP_RAA_VOUT 0xaa 778c2ecf20Sopenharmony_ci#define MT9V111_IFP_DECIMATION_MASK GENMASK(9, 0) 788c2ecf20Sopenharmony_ci#define MT9V111_IFP_DECIMATION_FREEZE BIT(15) 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci#define MT9V111_CORE_R03_WIN_HEIGHT 0x03 818c2ecf20Sopenharmony_ci#define MT9V111_CORE_R03_WIN_V_OFFS 2 828c2ecf20Sopenharmony_ci#define MT9V111_CORE_R04_WIN_WIDTH 0x04 838c2ecf20Sopenharmony_ci#define MT9V111_CORE_R04_WIN_H_OFFS 114 848c2ecf20Sopenharmony_ci#define MT9V111_CORE_R05_HBLANK 0x05 858c2ecf20Sopenharmony_ci#define MT9V111_CORE_R05_MIN_HBLANK 0x09 868c2ecf20Sopenharmony_ci#define MT9V111_CORE_R05_MAX_HBLANK GENMASK(9, 0) 878c2ecf20Sopenharmony_ci#define MT9V111_CORE_R05_DEF_HBLANK 0x26 888c2ecf20Sopenharmony_ci#define MT9V111_CORE_R06_VBLANK 0x06 898c2ecf20Sopenharmony_ci#define MT9V111_CORE_R06_MIN_VBLANK 0x03 908c2ecf20Sopenharmony_ci#define MT9V111_CORE_R06_MAX_VBLANK GENMASK(11, 0) 918c2ecf20Sopenharmony_ci#define MT9V111_CORE_R06_DEF_VBLANK 0x04 928c2ecf20Sopenharmony_ci#define MT9V111_CORE_R07_OUT_CTRL 0x07 938c2ecf20Sopenharmony_ci#define MT9V111_CORE_R07_OUT_CTRL_SAMPLE BIT(4) 948c2ecf20Sopenharmony_ci#define MT9V111_CORE_R09_PIXEL_INT 0x09 958c2ecf20Sopenharmony_ci#define MT9V111_CORE_R09_PIXEL_INT_MASK GENMASK(11, 0) 968c2ecf20Sopenharmony_ci#define MT9V111_CORE_R0D_CORE_RESET 0x0d 978c2ecf20Sopenharmony_ci#define MT9V111_CORE_R0D_CORE_RESET_MASK BIT(0) 988c2ecf20Sopenharmony_ci#define MT9V111_CORE_RFF_CHIP_VER 0xff 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci#define MT9V111_PIXEL_ARRAY_WIDTH 640 1018c2ecf20Sopenharmony_ci#define MT9V111_PIXEL_ARRAY_HEIGHT 480 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci#define MT9V111_MAX_CLKIN 27000000 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci/* The default sensor configuration at startup time. */ 1068c2ecf20Sopenharmony_cistatic const struct v4l2_mbus_framefmt mt9v111_def_fmt = { 1078c2ecf20Sopenharmony_ci .width = 640, 1088c2ecf20Sopenharmony_ci .height = 480, 1098c2ecf20Sopenharmony_ci .code = MEDIA_BUS_FMT_UYVY8_2X8, 1108c2ecf20Sopenharmony_ci .field = V4L2_FIELD_NONE, 1118c2ecf20Sopenharmony_ci .colorspace = V4L2_COLORSPACE_SRGB, 1128c2ecf20Sopenharmony_ci .ycbcr_enc = V4L2_YCBCR_ENC_601, 1138c2ecf20Sopenharmony_ci .quantization = V4L2_QUANTIZATION_LIM_RANGE, 1148c2ecf20Sopenharmony_ci .xfer_func = V4L2_XFER_FUNC_SRGB, 1158c2ecf20Sopenharmony_ci}; 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_cistruct mt9v111_dev { 1188c2ecf20Sopenharmony_ci struct device *dev; 1198c2ecf20Sopenharmony_ci struct i2c_client *client; 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci u8 addr_space; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci struct v4l2_subdev sd; 1248c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_MEDIA_CONTROLLER) 1258c2ecf20Sopenharmony_ci struct media_pad pad; 1268c2ecf20Sopenharmony_ci#endif 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci struct v4l2_ctrl *auto_awb; 1298c2ecf20Sopenharmony_ci struct v4l2_ctrl *auto_exp; 1308c2ecf20Sopenharmony_ci struct v4l2_ctrl *hblank; 1318c2ecf20Sopenharmony_ci struct v4l2_ctrl *vblank; 1328c2ecf20Sopenharmony_ci struct v4l2_ctrl_handler ctrls; 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci /* Output image format and sizes. */ 1358c2ecf20Sopenharmony_ci struct v4l2_mbus_framefmt fmt; 1368c2ecf20Sopenharmony_ci unsigned int fps; 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci /* Protects power up/down sequences. */ 1398c2ecf20Sopenharmony_ci struct mutex pwr_mutex; 1408c2ecf20Sopenharmony_ci int pwr_count; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci /* Protects stream on/off sequences. */ 1438c2ecf20Sopenharmony_ci struct mutex stream_mutex; 1448c2ecf20Sopenharmony_ci bool streaming; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci /* Flags to mark HW settings as not yet applied. */ 1478c2ecf20Sopenharmony_ci bool pending; 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci /* Clock provider and system clock frequency. */ 1508c2ecf20Sopenharmony_ci struct clk *clk; 1518c2ecf20Sopenharmony_ci u32 sysclk; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci struct gpio_desc *oe; 1548c2ecf20Sopenharmony_ci struct gpio_desc *standby; 1558c2ecf20Sopenharmony_ci struct gpio_desc *reset; 1568c2ecf20Sopenharmony_ci}; 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci#define sd_to_mt9v111(__sd) container_of((__sd), struct mt9v111_dev, sd) 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci/* 1618c2ecf20Sopenharmony_ci * mt9v111_mbus_fmt - List all media bus formats supported by the driver. 1628c2ecf20Sopenharmony_ci * 1638c2ecf20Sopenharmony_ci * Only list the media bus code here. The image sizes are freely configurable 1648c2ecf20Sopenharmony_ci * in the pixel array sizes range. 1658c2ecf20Sopenharmony_ci * 1668c2ecf20Sopenharmony_ci * The desired frame interval, in the supported frame interval range, is 1678c2ecf20Sopenharmony_ci * obtained by configuring blanking as the sensor does not have a PLL but 1688c2ecf20Sopenharmony_ci * only a fixed clock divider that generates the output pixel clock. 1698c2ecf20Sopenharmony_ci */ 1708c2ecf20Sopenharmony_cistatic struct mt9v111_mbus_fmt { 1718c2ecf20Sopenharmony_ci u32 code; 1728c2ecf20Sopenharmony_ci} mt9v111_formats[] = { 1738c2ecf20Sopenharmony_ci { 1748c2ecf20Sopenharmony_ci .code = MEDIA_BUS_FMT_UYVY8_2X8, 1758c2ecf20Sopenharmony_ci }, 1768c2ecf20Sopenharmony_ci { 1778c2ecf20Sopenharmony_ci .code = MEDIA_BUS_FMT_YUYV8_2X8, 1788c2ecf20Sopenharmony_ci }, 1798c2ecf20Sopenharmony_ci { 1808c2ecf20Sopenharmony_ci .code = MEDIA_BUS_FMT_VYUY8_2X8, 1818c2ecf20Sopenharmony_ci }, 1828c2ecf20Sopenharmony_ci { 1838c2ecf20Sopenharmony_ci .code = MEDIA_BUS_FMT_YVYU8_2X8, 1848c2ecf20Sopenharmony_ci }, 1858c2ecf20Sopenharmony_ci}; 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_cistatic u32 mt9v111_frame_intervals[] = {5, 10, 15, 20, 30}; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci/* 1908c2ecf20Sopenharmony_ci * mt9v111_frame_sizes - List sensor's supported resolutions. 1918c2ecf20Sopenharmony_ci * 1928c2ecf20Sopenharmony_ci * Resolution generated through decimation in the IFP block from the 1938c2ecf20Sopenharmony_ci * full VGA pixel array. 1948c2ecf20Sopenharmony_ci */ 1958c2ecf20Sopenharmony_cistatic struct v4l2_rect mt9v111_frame_sizes[] = { 1968c2ecf20Sopenharmony_ci { 1978c2ecf20Sopenharmony_ci .width = 640, 1988c2ecf20Sopenharmony_ci .height = 480, 1998c2ecf20Sopenharmony_ci }, 2008c2ecf20Sopenharmony_ci { 2018c2ecf20Sopenharmony_ci .width = 352, 2028c2ecf20Sopenharmony_ci .height = 288 2038c2ecf20Sopenharmony_ci }, 2048c2ecf20Sopenharmony_ci { 2058c2ecf20Sopenharmony_ci .width = 320, 2068c2ecf20Sopenharmony_ci .height = 240, 2078c2ecf20Sopenharmony_ci }, 2088c2ecf20Sopenharmony_ci { 2098c2ecf20Sopenharmony_ci .width = 176, 2108c2ecf20Sopenharmony_ci .height = 144, 2118c2ecf20Sopenharmony_ci }, 2128c2ecf20Sopenharmony_ci { 2138c2ecf20Sopenharmony_ci .width = 160, 2148c2ecf20Sopenharmony_ci .height = 120, 2158c2ecf20Sopenharmony_ci }, 2168c2ecf20Sopenharmony_ci}; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci/* --- Device I/O access --- */ 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_cistatic int __mt9v111_read(struct i2c_client *c, u8 reg, u16 *val) 2218c2ecf20Sopenharmony_ci{ 2228c2ecf20Sopenharmony_ci struct i2c_msg msg[2]; 2238c2ecf20Sopenharmony_ci __be16 buf; 2248c2ecf20Sopenharmony_ci int ret; 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci msg[0].addr = c->addr; 2278c2ecf20Sopenharmony_ci msg[0].flags = 0; 2288c2ecf20Sopenharmony_ci msg[0].len = 1; 2298c2ecf20Sopenharmony_ci msg[0].buf = ® 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci msg[1].addr = c->addr; 2328c2ecf20Sopenharmony_ci msg[1].flags = I2C_M_RD; 2338c2ecf20Sopenharmony_ci msg[1].len = 2; 2348c2ecf20Sopenharmony_ci msg[1].buf = (char *)&buf; 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci ret = i2c_transfer(c->adapter, msg, 2); 2378c2ecf20Sopenharmony_ci if (ret < 0) { 2388c2ecf20Sopenharmony_ci dev_err(&c->dev, "i2c read transfer error: %d\n", ret); 2398c2ecf20Sopenharmony_ci return ret; 2408c2ecf20Sopenharmony_ci } 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci *val = be16_to_cpu(buf); 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci dev_dbg(&c->dev, "%s: %x=%x\n", __func__, reg, *val); 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci return 0; 2478c2ecf20Sopenharmony_ci} 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_cistatic int __mt9v111_write(struct i2c_client *c, u8 reg, u16 val) 2508c2ecf20Sopenharmony_ci{ 2518c2ecf20Sopenharmony_ci struct i2c_msg msg; 2528c2ecf20Sopenharmony_ci u8 buf[3] = { 0 }; 2538c2ecf20Sopenharmony_ci int ret; 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci buf[0] = reg; 2568c2ecf20Sopenharmony_ci buf[1] = val >> 8; 2578c2ecf20Sopenharmony_ci buf[2] = val & 0xff; 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci msg.addr = c->addr; 2608c2ecf20Sopenharmony_ci msg.flags = 0; 2618c2ecf20Sopenharmony_ci msg.len = 3; 2628c2ecf20Sopenharmony_ci msg.buf = (char *)buf; 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci dev_dbg(&c->dev, "%s: %x = %x%x\n", __func__, reg, buf[1], buf[2]); 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci ret = i2c_transfer(c->adapter, &msg, 1); 2678c2ecf20Sopenharmony_ci if (ret < 0) { 2688c2ecf20Sopenharmony_ci dev_err(&c->dev, "i2c write transfer error: %d\n", ret); 2698c2ecf20Sopenharmony_ci return ret; 2708c2ecf20Sopenharmony_ci } 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci return 0; 2738c2ecf20Sopenharmony_ci} 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_cistatic int __mt9v111_addr_space_select(struct i2c_client *c, u16 addr_space) 2768c2ecf20Sopenharmony_ci{ 2778c2ecf20Sopenharmony_ci struct v4l2_subdev *sd = i2c_get_clientdata(c); 2788c2ecf20Sopenharmony_ci struct mt9v111_dev *mt9v111 = sd_to_mt9v111(sd); 2798c2ecf20Sopenharmony_ci u16 val; 2808c2ecf20Sopenharmony_ci int ret; 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci if (mt9v111->addr_space == addr_space) 2838c2ecf20Sopenharmony_ci return 0; 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci ret = __mt9v111_write(c, MT9V111_R01_ADDR_SPACE, addr_space); 2868c2ecf20Sopenharmony_ci if (ret) 2878c2ecf20Sopenharmony_ci return ret; 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci /* Verify address space has been updated */ 2908c2ecf20Sopenharmony_ci ret = __mt9v111_read(c, MT9V111_R01_ADDR_SPACE, &val); 2918c2ecf20Sopenharmony_ci if (ret) 2928c2ecf20Sopenharmony_ci return ret; 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci if (val != addr_space) 2958c2ecf20Sopenharmony_ci return -EINVAL; 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci mt9v111->addr_space = addr_space; 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci return 0; 3008c2ecf20Sopenharmony_ci} 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_cistatic int mt9v111_read(struct i2c_client *c, u8 addr_space, u8 reg, u16 *val) 3038c2ecf20Sopenharmony_ci{ 3048c2ecf20Sopenharmony_ci int ret; 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci /* Select register address space first. */ 3078c2ecf20Sopenharmony_ci ret = __mt9v111_addr_space_select(c, addr_space); 3088c2ecf20Sopenharmony_ci if (ret) 3098c2ecf20Sopenharmony_ci return ret; 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci ret = __mt9v111_read(c, reg, val); 3128c2ecf20Sopenharmony_ci if (ret) 3138c2ecf20Sopenharmony_ci return ret; 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci return 0; 3168c2ecf20Sopenharmony_ci} 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_cistatic int mt9v111_write(struct i2c_client *c, u8 addr_space, u8 reg, u16 val) 3198c2ecf20Sopenharmony_ci{ 3208c2ecf20Sopenharmony_ci int ret; 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci /* Select register address space first. */ 3238c2ecf20Sopenharmony_ci ret = __mt9v111_addr_space_select(c, addr_space); 3248c2ecf20Sopenharmony_ci if (ret) 3258c2ecf20Sopenharmony_ci return ret; 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci ret = __mt9v111_write(c, reg, val); 3288c2ecf20Sopenharmony_ci if (ret) 3298c2ecf20Sopenharmony_ci return ret; 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci return 0; 3328c2ecf20Sopenharmony_ci} 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_cistatic int mt9v111_update(struct i2c_client *c, u8 addr_space, u8 reg, 3358c2ecf20Sopenharmony_ci u16 mask, u16 val) 3368c2ecf20Sopenharmony_ci{ 3378c2ecf20Sopenharmony_ci u16 current_val; 3388c2ecf20Sopenharmony_ci int ret; 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci /* Select register address space first. */ 3418c2ecf20Sopenharmony_ci ret = __mt9v111_addr_space_select(c, addr_space); 3428c2ecf20Sopenharmony_ci if (ret) 3438c2ecf20Sopenharmony_ci return ret; 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci /* Read the current register value, then update it. */ 3468c2ecf20Sopenharmony_ci ret = __mt9v111_read(c, reg, ¤t_val); 3478c2ecf20Sopenharmony_ci if (ret) 3488c2ecf20Sopenharmony_ci return ret; 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci current_val &= ~mask; 3518c2ecf20Sopenharmony_ci current_val |= (val & mask); 3528c2ecf20Sopenharmony_ci ret = __mt9v111_write(c, reg, current_val); 3538c2ecf20Sopenharmony_ci if (ret) 3548c2ecf20Sopenharmony_ci return ret; 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci return 0; 3578c2ecf20Sopenharmony_ci} 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci/* --- Sensor HW operations --- */ 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_cistatic int __mt9v111_power_on(struct v4l2_subdev *sd) 3628c2ecf20Sopenharmony_ci{ 3638c2ecf20Sopenharmony_ci struct mt9v111_dev *mt9v111 = sd_to_mt9v111(sd); 3648c2ecf20Sopenharmony_ci int ret; 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci ret = clk_prepare_enable(mt9v111->clk); 3678c2ecf20Sopenharmony_ci if (ret) 3688c2ecf20Sopenharmony_ci return ret; 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci clk_set_rate(mt9v111->clk, mt9v111->sysclk); 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci gpiod_set_value(mt9v111->standby, 0); 3738c2ecf20Sopenharmony_ci usleep_range(500, 1000); 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci gpiod_set_value(mt9v111->oe, 1); 3768c2ecf20Sopenharmony_ci usleep_range(500, 1000); 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci return 0; 3798c2ecf20Sopenharmony_ci} 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_cistatic int __mt9v111_power_off(struct v4l2_subdev *sd) 3828c2ecf20Sopenharmony_ci{ 3838c2ecf20Sopenharmony_ci struct mt9v111_dev *mt9v111 = sd_to_mt9v111(sd); 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci gpiod_set_value(mt9v111->oe, 0); 3868c2ecf20Sopenharmony_ci usleep_range(500, 1000); 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci gpiod_set_value(mt9v111->standby, 1); 3898c2ecf20Sopenharmony_ci usleep_range(500, 1000); 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci clk_disable_unprepare(mt9v111->clk); 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci return 0; 3948c2ecf20Sopenharmony_ci} 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_cistatic int __mt9v111_hw_reset(struct mt9v111_dev *mt9v111) 3978c2ecf20Sopenharmony_ci{ 3988c2ecf20Sopenharmony_ci if (!mt9v111->reset) 3998c2ecf20Sopenharmony_ci return -EINVAL; 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci gpiod_set_value(mt9v111->reset, 1); 4028c2ecf20Sopenharmony_ci usleep_range(500, 1000); 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci gpiod_set_value(mt9v111->reset, 0); 4058c2ecf20Sopenharmony_ci usleep_range(500, 1000); 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci return 0; 4088c2ecf20Sopenharmony_ci} 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_cistatic int __mt9v111_sw_reset(struct mt9v111_dev *mt9v111) 4118c2ecf20Sopenharmony_ci{ 4128c2ecf20Sopenharmony_ci struct i2c_client *c = mt9v111->client; 4138c2ecf20Sopenharmony_ci int ret; 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci /* Software reset core and IFP blocks. */ 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci ret = mt9v111_update(c, MT9V111_R01_CORE, 4188c2ecf20Sopenharmony_ci MT9V111_CORE_R0D_CORE_RESET, 4198c2ecf20Sopenharmony_ci MT9V111_CORE_R0D_CORE_RESET_MASK, 1); 4208c2ecf20Sopenharmony_ci if (ret) 4218c2ecf20Sopenharmony_ci return ret; 4228c2ecf20Sopenharmony_ci usleep_range(500, 1000); 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci ret = mt9v111_update(c, MT9V111_R01_CORE, 4258c2ecf20Sopenharmony_ci MT9V111_CORE_R0D_CORE_RESET, 4268c2ecf20Sopenharmony_ci MT9V111_CORE_R0D_CORE_RESET_MASK, 0); 4278c2ecf20Sopenharmony_ci if (ret) 4288c2ecf20Sopenharmony_ci return ret; 4298c2ecf20Sopenharmony_ci usleep_range(500, 1000); 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci ret = mt9v111_update(c, MT9V111_R01_IFP, 4328c2ecf20Sopenharmony_ci MT9V111_IFP_R07_IFP_RESET, 4338c2ecf20Sopenharmony_ci MT9V111_IFP_R07_IFP_RESET_MASK, 1); 4348c2ecf20Sopenharmony_ci if (ret) 4358c2ecf20Sopenharmony_ci return ret; 4368c2ecf20Sopenharmony_ci usleep_range(500, 1000); 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci ret = mt9v111_update(c, MT9V111_R01_IFP, 4398c2ecf20Sopenharmony_ci MT9V111_IFP_R07_IFP_RESET, 4408c2ecf20Sopenharmony_ci MT9V111_IFP_R07_IFP_RESET_MASK, 0); 4418c2ecf20Sopenharmony_ci if (ret) 4428c2ecf20Sopenharmony_ci return ret; 4438c2ecf20Sopenharmony_ci usleep_range(500, 1000); 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci return 0; 4468c2ecf20Sopenharmony_ci} 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_cistatic int mt9v111_calc_frame_rate(struct mt9v111_dev *mt9v111, 4498c2ecf20Sopenharmony_ci struct v4l2_fract *tpf) 4508c2ecf20Sopenharmony_ci{ 4518c2ecf20Sopenharmony_ci unsigned int fps = tpf->numerator ? 4528c2ecf20Sopenharmony_ci tpf->denominator / tpf->numerator : 4538c2ecf20Sopenharmony_ci tpf->denominator; 4548c2ecf20Sopenharmony_ci unsigned int best_diff; 4558c2ecf20Sopenharmony_ci unsigned int frm_cols; 4568c2ecf20Sopenharmony_ci unsigned int row_pclk; 4578c2ecf20Sopenharmony_ci unsigned int best_fps; 4588c2ecf20Sopenharmony_ci unsigned int pclk; 4598c2ecf20Sopenharmony_ci unsigned int diff; 4608c2ecf20Sopenharmony_ci unsigned int idx; 4618c2ecf20Sopenharmony_ci unsigned int hb; 4628c2ecf20Sopenharmony_ci unsigned int vb; 4638c2ecf20Sopenharmony_ci unsigned int i; 4648c2ecf20Sopenharmony_ci int ret; 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci /* Approximate to the closest supported frame interval. */ 4678c2ecf20Sopenharmony_ci best_diff = ~0L; 4688c2ecf20Sopenharmony_ci for (i = 0, idx = 0; i < ARRAY_SIZE(mt9v111_frame_intervals); i++) { 4698c2ecf20Sopenharmony_ci diff = abs(fps - mt9v111_frame_intervals[i]); 4708c2ecf20Sopenharmony_ci if (diff < best_diff) { 4718c2ecf20Sopenharmony_ci idx = i; 4728c2ecf20Sopenharmony_ci best_diff = diff; 4738c2ecf20Sopenharmony_ci } 4748c2ecf20Sopenharmony_ci } 4758c2ecf20Sopenharmony_ci fps = mt9v111_frame_intervals[idx]; 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci /* 4788c2ecf20Sopenharmony_ci * The sensor does not provide a PLL circuitry and pixel clock is 4798c2ecf20Sopenharmony_ci * generated dividing the master clock source by two. 4808c2ecf20Sopenharmony_ci * 4818c2ecf20Sopenharmony_ci * Trow = (W + Hblank + 114) * 2 * (1 / SYSCLK) 4828c2ecf20Sopenharmony_ci * TFrame = Trow * (H + Vblank + 2) 4838c2ecf20Sopenharmony_ci * 4848c2ecf20Sopenharmony_ci * FPS = (SYSCLK / 2) / (Trow * (H + Vblank + 2)) 4858c2ecf20Sopenharmony_ci * 4868c2ecf20Sopenharmony_ci * This boils down to tune H and V blanks to best approximate the 4878c2ecf20Sopenharmony_ci * above equation. 4888c2ecf20Sopenharmony_ci * 4898c2ecf20Sopenharmony_ci * Test all available H/V blank values, until we reach the 4908c2ecf20Sopenharmony_ci * desired frame rate. 4918c2ecf20Sopenharmony_ci */ 4928c2ecf20Sopenharmony_ci best_fps = vb = hb = 0; 4938c2ecf20Sopenharmony_ci pclk = DIV_ROUND_CLOSEST(mt9v111->sysclk, 2); 4948c2ecf20Sopenharmony_ci row_pclk = MT9V111_PIXEL_ARRAY_WIDTH + 7 + MT9V111_CORE_R04_WIN_H_OFFS; 4958c2ecf20Sopenharmony_ci frm_cols = MT9V111_PIXEL_ARRAY_HEIGHT + 7 + MT9V111_CORE_R03_WIN_V_OFFS; 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci best_diff = ~0L; 4988c2ecf20Sopenharmony_ci for (vb = MT9V111_CORE_R06_MIN_VBLANK; 4998c2ecf20Sopenharmony_ci vb < MT9V111_CORE_R06_MAX_VBLANK; vb++) { 5008c2ecf20Sopenharmony_ci for (hb = MT9V111_CORE_R05_MIN_HBLANK; 5018c2ecf20Sopenharmony_ci hb < MT9V111_CORE_R05_MAX_HBLANK; hb += 10) { 5028c2ecf20Sopenharmony_ci unsigned int t_frame = (row_pclk + hb) * 5038c2ecf20Sopenharmony_ci (frm_cols + vb); 5048c2ecf20Sopenharmony_ci unsigned int t_fps = DIV_ROUND_CLOSEST(pclk, t_frame); 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_ci diff = abs(fps - t_fps); 5078c2ecf20Sopenharmony_ci if (diff < best_diff) { 5088c2ecf20Sopenharmony_ci best_diff = diff; 5098c2ecf20Sopenharmony_ci best_fps = t_fps; 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci if (diff == 0) 5128c2ecf20Sopenharmony_ci break; 5138c2ecf20Sopenharmony_ci } 5148c2ecf20Sopenharmony_ci } 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci if (diff == 0) 5178c2ecf20Sopenharmony_ci break; 5188c2ecf20Sopenharmony_ci } 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ci ret = v4l2_ctrl_s_ctrl_int64(mt9v111->hblank, hb); 5218c2ecf20Sopenharmony_ci if (ret) 5228c2ecf20Sopenharmony_ci return ret; 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci ret = v4l2_ctrl_s_ctrl_int64(mt9v111->vblank, vb); 5258c2ecf20Sopenharmony_ci if (ret) 5268c2ecf20Sopenharmony_ci return ret; 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_ci tpf->numerator = 1; 5298c2ecf20Sopenharmony_ci tpf->denominator = best_fps; 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_ci return 0; 5328c2ecf20Sopenharmony_ci} 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_cistatic int mt9v111_hw_config(struct mt9v111_dev *mt9v111) 5358c2ecf20Sopenharmony_ci{ 5368c2ecf20Sopenharmony_ci struct i2c_client *c = mt9v111->client; 5378c2ecf20Sopenharmony_ci unsigned int ret; 5388c2ecf20Sopenharmony_ci u16 outfmtctrl2; 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ci /* Force device reset. */ 5418c2ecf20Sopenharmony_ci ret = __mt9v111_hw_reset(mt9v111); 5428c2ecf20Sopenharmony_ci if (ret == -EINVAL) 5438c2ecf20Sopenharmony_ci ret = __mt9v111_sw_reset(mt9v111); 5448c2ecf20Sopenharmony_ci if (ret) 5458c2ecf20Sopenharmony_ci return ret; 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ci /* Configure internal clock sample rate. */ 5488c2ecf20Sopenharmony_ci ret = mt9v111->sysclk < DIV_ROUND_CLOSEST(MT9V111_MAX_CLKIN, 2) ? 5498c2ecf20Sopenharmony_ci mt9v111_update(c, MT9V111_R01_CORE, 5508c2ecf20Sopenharmony_ci MT9V111_CORE_R07_OUT_CTRL, 5518c2ecf20Sopenharmony_ci MT9V111_CORE_R07_OUT_CTRL_SAMPLE, 1) : 5528c2ecf20Sopenharmony_ci mt9v111_update(c, MT9V111_R01_CORE, 5538c2ecf20Sopenharmony_ci MT9V111_CORE_R07_OUT_CTRL, 5548c2ecf20Sopenharmony_ci MT9V111_CORE_R07_OUT_CTRL_SAMPLE, 0); 5558c2ecf20Sopenharmony_ci if (ret) 5568c2ecf20Sopenharmony_ci return ret; 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_ci /* 5598c2ecf20Sopenharmony_ci * Configure output image format components ordering. 5608c2ecf20Sopenharmony_ci * 5618c2ecf20Sopenharmony_ci * TODO: IFP block can also output several RGB permutations, we only 5628c2ecf20Sopenharmony_ci * support YUYV permutations at the moment. 5638c2ecf20Sopenharmony_ci */ 5648c2ecf20Sopenharmony_ci switch (mt9v111->fmt.code) { 5658c2ecf20Sopenharmony_ci case MEDIA_BUS_FMT_YUYV8_2X8: 5668c2ecf20Sopenharmony_ci outfmtctrl2 = MT9V111_IFP_R3A_OUTFMT_CTRL2_SWAP_YC; 5678c2ecf20Sopenharmony_ci break; 5688c2ecf20Sopenharmony_ci case MEDIA_BUS_FMT_VYUY8_2X8: 5698c2ecf20Sopenharmony_ci outfmtctrl2 = MT9V111_IFP_R3A_OUTFMT_CTRL2_SWAP_CBCR; 5708c2ecf20Sopenharmony_ci break; 5718c2ecf20Sopenharmony_ci case MEDIA_BUS_FMT_YVYU8_2X8: 5728c2ecf20Sopenharmony_ci outfmtctrl2 = MT9V111_IFP_R3A_OUTFMT_CTRL2_SWAP_YC | 5738c2ecf20Sopenharmony_ci MT9V111_IFP_R3A_OUTFMT_CTRL2_SWAP_CBCR; 5748c2ecf20Sopenharmony_ci break; 5758c2ecf20Sopenharmony_ci case MEDIA_BUS_FMT_UYVY8_2X8: 5768c2ecf20Sopenharmony_ci default: 5778c2ecf20Sopenharmony_ci outfmtctrl2 = 0; 5788c2ecf20Sopenharmony_ci break; 5798c2ecf20Sopenharmony_ci } 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_ci ret = mt9v111_update(c, MT9V111_R01_IFP, MT9V111_IFP_R3A_OUTFMT_CTRL2, 5828c2ecf20Sopenharmony_ci MT9V111_IFP_R3A_OUTFMT_CTRL2_SWAP_MASK, 5838c2ecf20Sopenharmony_ci outfmtctrl2); 5848c2ecf20Sopenharmony_ci if (ret) 5858c2ecf20Sopenharmony_ci return ret; 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ci /* 5888c2ecf20Sopenharmony_ci * Do not change default sensor's core configuration: 5898c2ecf20Sopenharmony_ci * output the whole 640x480 pixel array, skip 18 columns and 6 rows. 5908c2ecf20Sopenharmony_ci * 5918c2ecf20Sopenharmony_ci * Instead, control the output image size through IFP block. 5928c2ecf20Sopenharmony_ci * 5938c2ecf20Sopenharmony_ci * TODO: No zoom&pan support. Currently we control the output image 5948c2ecf20Sopenharmony_ci * size only through decimation, with no zoom support. 5958c2ecf20Sopenharmony_ci */ 5968c2ecf20Sopenharmony_ci ret = mt9v111_write(c, MT9V111_R01_IFP, MT9V111_IFP_RA5_HPAN, 5978c2ecf20Sopenharmony_ci MT9V111_IFP_DECIMATION_FREEZE); 5988c2ecf20Sopenharmony_ci if (ret) 5998c2ecf20Sopenharmony_ci return ret; 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_ci ret = mt9v111_write(c, MT9V111_R01_IFP, MT9V111_IFP_RA8_VPAN, 6028c2ecf20Sopenharmony_ci MT9V111_IFP_DECIMATION_FREEZE); 6038c2ecf20Sopenharmony_ci if (ret) 6048c2ecf20Sopenharmony_ci return ret; 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_ci ret = mt9v111_write(c, MT9V111_R01_IFP, MT9V111_IFP_RA6_HZOOM, 6078c2ecf20Sopenharmony_ci MT9V111_IFP_DECIMATION_FREEZE | 6088c2ecf20Sopenharmony_ci MT9V111_PIXEL_ARRAY_WIDTH); 6098c2ecf20Sopenharmony_ci if (ret) 6108c2ecf20Sopenharmony_ci return ret; 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_ci ret = mt9v111_write(c, MT9V111_R01_IFP, MT9V111_IFP_RA9_VZOOM, 6138c2ecf20Sopenharmony_ci MT9V111_IFP_DECIMATION_FREEZE | 6148c2ecf20Sopenharmony_ci MT9V111_PIXEL_ARRAY_HEIGHT); 6158c2ecf20Sopenharmony_ci if (ret) 6168c2ecf20Sopenharmony_ci return ret; 6178c2ecf20Sopenharmony_ci 6188c2ecf20Sopenharmony_ci ret = mt9v111_write(c, MT9V111_R01_IFP, MT9V111_IFP_RA7_HOUT, 6198c2ecf20Sopenharmony_ci MT9V111_IFP_DECIMATION_FREEZE | 6208c2ecf20Sopenharmony_ci mt9v111->fmt.width); 6218c2ecf20Sopenharmony_ci if (ret) 6228c2ecf20Sopenharmony_ci return ret; 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_ci ret = mt9v111_write(c, MT9V111_R01_IFP, MT9V111_IFP_RAA_VOUT, 6258c2ecf20Sopenharmony_ci mt9v111->fmt.height); 6268c2ecf20Sopenharmony_ci if (ret) 6278c2ecf20Sopenharmony_ci return ret; 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ci /* Apply controls to set auto exp, auto awb and timings */ 6308c2ecf20Sopenharmony_ci ret = v4l2_ctrl_handler_setup(&mt9v111->ctrls); 6318c2ecf20Sopenharmony_ci if (ret) 6328c2ecf20Sopenharmony_ci return ret; 6338c2ecf20Sopenharmony_ci 6348c2ecf20Sopenharmony_ci /* 6358c2ecf20Sopenharmony_ci * Set pixel integration time to the whole frame time. 6368c2ecf20Sopenharmony_ci * This value controls the the shutter delay when running with AE 6378c2ecf20Sopenharmony_ci * disabled. If longer than frame time, it affects the output 6388c2ecf20Sopenharmony_ci * frame rate. 6398c2ecf20Sopenharmony_ci */ 6408c2ecf20Sopenharmony_ci return mt9v111_write(c, MT9V111_R01_CORE, MT9V111_CORE_R09_PIXEL_INT, 6418c2ecf20Sopenharmony_ci MT9V111_PIXEL_ARRAY_HEIGHT); 6428c2ecf20Sopenharmony_ci} 6438c2ecf20Sopenharmony_ci 6448c2ecf20Sopenharmony_ci/* --- V4L2 subdev operations --- */ 6458c2ecf20Sopenharmony_ci 6468c2ecf20Sopenharmony_cistatic int mt9v111_s_power(struct v4l2_subdev *sd, int on) 6478c2ecf20Sopenharmony_ci{ 6488c2ecf20Sopenharmony_ci struct mt9v111_dev *mt9v111 = sd_to_mt9v111(sd); 6498c2ecf20Sopenharmony_ci int pwr_count; 6508c2ecf20Sopenharmony_ci int ret = 0; 6518c2ecf20Sopenharmony_ci 6528c2ecf20Sopenharmony_ci mutex_lock(&mt9v111->pwr_mutex); 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_ci /* 6558c2ecf20Sopenharmony_ci * Make sure we're transitioning from 0 to 1, or viceversa, 6568c2ecf20Sopenharmony_ci * before actually changing the power state. 6578c2ecf20Sopenharmony_ci */ 6588c2ecf20Sopenharmony_ci pwr_count = mt9v111->pwr_count; 6598c2ecf20Sopenharmony_ci pwr_count += on ? 1 : -1; 6608c2ecf20Sopenharmony_ci if (pwr_count == !!on) { 6618c2ecf20Sopenharmony_ci ret = on ? __mt9v111_power_on(sd) : 6628c2ecf20Sopenharmony_ci __mt9v111_power_off(sd); 6638c2ecf20Sopenharmony_ci if (!ret) 6648c2ecf20Sopenharmony_ci /* All went well, updated power counter. */ 6658c2ecf20Sopenharmony_ci mt9v111->pwr_count = pwr_count; 6668c2ecf20Sopenharmony_ci 6678c2ecf20Sopenharmony_ci mutex_unlock(&mt9v111->pwr_mutex); 6688c2ecf20Sopenharmony_ci 6698c2ecf20Sopenharmony_ci return ret; 6708c2ecf20Sopenharmony_ci } 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_ci /* 6738c2ecf20Sopenharmony_ci * Update power counter to keep track of how many nested calls we 6748c2ecf20Sopenharmony_ci * received. 6758c2ecf20Sopenharmony_ci */ 6768c2ecf20Sopenharmony_ci WARN_ON(pwr_count < 0 || pwr_count > 1); 6778c2ecf20Sopenharmony_ci mt9v111->pwr_count = pwr_count; 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_ci mutex_unlock(&mt9v111->pwr_mutex); 6808c2ecf20Sopenharmony_ci 6818c2ecf20Sopenharmony_ci return ret; 6828c2ecf20Sopenharmony_ci} 6838c2ecf20Sopenharmony_ci 6848c2ecf20Sopenharmony_cistatic int mt9v111_s_stream(struct v4l2_subdev *subdev, int enable) 6858c2ecf20Sopenharmony_ci{ 6868c2ecf20Sopenharmony_ci struct mt9v111_dev *mt9v111 = sd_to_mt9v111(subdev); 6878c2ecf20Sopenharmony_ci int ret; 6888c2ecf20Sopenharmony_ci 6898c2ecf20Sopenharmony_ci mutex_lock(&mt9v111->stream_mutex); 6908c2ecf20Sopenharmony_ci 6918c2ecf20Sopenharmony_ci if (mt9v111->streaming == enable) { 6928c2ecf20Sopenharmony_ci mutex_unlock(&mt9v111->stream_mutex); 6938c2ecf20Sopenharmony_ci return 0; 6948c2ecf20Sopenharmony_ci } 6958c2ecf20Sopenharmony_ci 6968c2ecf20Sopenharmony_ci ret = mt9v111_s_power(subdev, enable); 6978c2ecf20Sopenharmony_ci if (ret) 6988c2ecf20Sopenharmony_ci goto error_unlock; 6998c2ecf20Sopenharmony_ci 7008c2ecf20Sopenharmony_ci if (enable && mt9v111->pending) { 7018c2ecf20Sopenharmony_ci ret = mt9v111_hw_config(mt9v111); 7028c2ecf20Sopenharmony_ci if (ret) 7038c2ecf20Sopenharmony_ci goto error_unlock; 7048c2ecf20Sopenharmony_ci 7058c2ecf20Sopenharmony_ci /* 7068c2ecf20Sopenharmony_ci * No need to update control here as far as only H/VBLANK are 7078c2ecf20Sopenharmony_ci * supported and immediately programmed to registers in .s_ctrl 7088c2ecf20Sopenharmony_ci */ 7098c2ecf20Sopenharmony_ci 7108c2ecf20Sopenharmony_ci mt9v111->pending = false; 7118c2ecf20Sopenharmony_ci } 7128c2ecf20Sopenharmony_ci 7138c2ecf20Sopenharmony_ci mt9v111->streaming = enable ? true : false; 7148c2ecf20Sopenharmony_ci mutex_unlock(&mt9v111->stream_mutex); 7158c2ecf20Sopenharmony_ci 7168c2ecf20Sopenharmony_ci return 0; 7178c2ecf20Sopenharmony_ci 7188c2ecf20Sopenharmony_cierror_unlock: 7198c2ecf20Sopenharmony_ci mutex_unlock(&mt9v111->stream_mutex); 7208c2ecf20Sopenharmony_ci 7218c2ecf20Sopenharmony_ci return ret; 7228c2ecf20Sopenharmony_ci} 7238c2ecf20Sopenharmony_ci 7248c2ecf20Sopenharmony_cistatic int mt9v111_s_frame_interval(struct v4l2_subdev *sd, 7258c2ecf20Sopenharmony_ci struct v4l2_subdev_frame_interval *ival) 7268c2ecf20Sopenharmony_ci{ 7278c2ecf20Sopenharmony_ci struct mt9v111_dev *mt9v111 = sd_to_mt9v111(sd); 7288c2ecf20Sopenharmony_ci struct v4l2_fract *tpf = &ival->interval; 7298c2ecf20Sopenharmony_ci unsigned int fps = tpf->numerator ? 7308c2ecf20Sopenharmony_ci tpf->denominator / tpf->numerator : 7318c2ecf20Sopenharmony_ci tpf->denominator; 7328c2ecf20Sopenharmony_ci unsigned int max_fps; 7338c2ecf20Sopenharmony_ci 7348c2ecf20Sopenharmony_ci if (!tpf->numerator) 7358c2ecf20Sopenharmony_ci tpf->numerator = 1; 7368c2ecf20Sopenharmony_ci 7378c2ecf20Sopenharmony_ci mutex_lock(&mt9v111->stream_mutex); 7388c2ecf20Sopenharmony_ci 7398c2ecf20Sopenharmony_ci if (mt9v111->streaming) { 7408c2ecf20Sopenharmony_ci mutex_unlock(&mt9v111->stream_mutex); 7418c2ecf20Sopenharmony_ci return -EBUSY; 7428c2ecf20Sopenharmony_ci } 7438c2ecf20Sopenharmony_ci 7448c2ecf20Sopenharmony_ci if (mt9v111->fps == fps) { 7458c2ecf20Sopenharmony_ci mutex_unlock(&mt9v111->stream_mutex); 7468c2ecf20Sopenharmony_ci return 0; 7478c2ecf20Sopenharmony_ci } 7488c2ecf20Sopenharmony_ci 7498c2ecf20Sopenharmony_ci /* Make sure frame rate/image sizes constraints are respected. */ 7508c2ecf20Sopenharmony_ci if (mt9v111->fmt.width < QVGA_WIDTH && 7518c2ecf20Sopenharmony_ci mt9v111->fmt.height < QVGA_HEIGHT) 7528c2ecf20Sopenharmony_ci max_fps = 90; 7538c2ecf20Sopenharmony_ci else if (mt9v111->fmt.width < CIF_WIDTH && 7548c2ecf20Sopenharmony_ci mt9v111->fmt.height < CIF_HEIGHT) 7558c2ecf20Sopenharmony_ci max_fps = 60; 7568c2ecf20Sopenharmony_ci else 7578c2ecf20Sopenharmony_ci max_fps = mt9v111->sysclk < 7588c2ecf20Sopenharmony_ci DIV_ROUND_CLOSEST(MT9V111_MAX_CLKIN, 2) ? 15 : 7598c2ecf20Sopenharmony_ci 30; 7608c2ecf20Sopenharmony_ci 7618c2ecf20Sopenharmony_ci if (fps > max_fps) { 7628c2ecf20Sopenharmony_ci mutex_unlock(&mt9v111->stream_mutex); 7638c2ecf20Sopenharmony_ci return -EINVAL; 7648c2ecf20Sopenharmony_ci } 7658c2ecf20Sopenharmony_ci 7668c2ecf20Sopenharmony_ci mt9v111_calc_frame_rate(mt9v111, tpf); 7678c2ecf20Sopenharmony_ci 7688c2ecf20Sopenharmony_ci mt9v111->fps = fps; 7698c2ecf20Sopenharmony_ci mt9v111->pending = true; 7708c2ecf20Sopenharmony_ci 7718c2ecf20Sopenharmony_ci mutex_unlock(&mt9v111->stream_mutex); 7728c2ecf20Sopenharmony_ci 7738c2ecf20Sopenharmony_ci return 0; 7748c2ecf20Sopenharmony_ci} 7758c2ecf20Sopenharmony_ci 7768c2ecf20Sopenharmony_cistatic int mt9v111_g_frame_interval(struct v4l2_subdev *sd, 7778c2ecf20Sopenharmony_ci struct v4l2_subdev_frame_interval *ival) 7788c2ecf20Sopenharmony_ci{ 7798c2ecf20Sopenharmony_ci struct mt9v111_dev *mt9v111 = sd_to_mt9v111(sd); 7808c2ecf20Sopenharmony_ci struct v4l2_fract *tpf = &ival->interval; 7818c2ecf20Sopenharmony_ci 7828c2ecf20Sopenharmony_ci mutex_lock(&mt9v111->stream_mutex); 7838c2ecf20Sopenharmony_ci 7848c2ecf20Sopenharmony_ci tpf->numerator = 1; 7858c2ecf20Sopenharmony_ci tpf->denominator = mt9v111->fps; 7868c2ecf20Sopenharmony_ci 7878c2ecf20Sopenharmony_ci mutex_unlock(&mt9v111->stream_mutex); 7888c2ecf20Sopenharmony_ci 7898c2ecf20Sopenharmony_ci return 0; 7908c2ecf20Sopenharmony_ci} 7918c2ecf20Sopenharmony_ci 7928c2ecf20Sopenharmony_cistatic struct v4l2_mbus_framefmt *__mt9v111_get_pad_format( 7938c2ecf20Sopenharmony_ci struct mt9v111_dev *mt9v111, 7948c2ecf20Sopenharmony_ci struct v4l2_subdev_pad_config *cfg, 7958c2ecf20Sopenharmony_ci unsigned int pad, 7968c2ecf20Sopenharmony_ci enum v4l2_subdev_format_whence which) 7978c2ecf20Sopenharmony_ci{ 7988c2ecf20Sopenharmony_ci switch (which) { 7998c2ecf20Sopenharmony_ci case V4L2_SUBDEV_FORMAT_TRY: 8008c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_VIDEO_V4L2_SUBDEV_API) 8018c2ecf20Sopenharmony_ci return v4l2_subdev_get_try_format(&mt9v111->sd, cfg, pad); 8028c2ecf20Sopenharmony_ci#else 8038c2ecf20Sopenharmony_ci return &cfg->try_fmt; 8048c2ecf20Sopenharmony_ci#endif 8058c2ecf20Sopenharmony_ci case V4L2_SUBDEV_FORMAT_ACTIVE: 8068c2ecf20Sopenharmony_ci return &mt9v111->fmt; 8078c2ecf20Sopenharmony_ci default: 8088c2ecf20Sopenharmony_ci return NULL; 8098c2ecf20Sopenharmony_ci } 8108c2ecf20Sopenharmony_ci} 8118c2ecf20Sopenharmony_ci 8128c2ecf20Sopenharmony_cistatic int mt9v111_enum_mbus_code(struct v4l2_subdev *subdev, 8138c2ecf20Sopenharmony_ci struct v4l2_subdev_pad_config *cfg, 8148c2ecf20Sopenharmony_ci struct v4l2_subdev_mbus_code_enum *code) 8158c2ecf20Sopenharmony_ci{ 8168c2ecf20Sopenharmony_ci if (code->pad || code->index > ARRAY_SIZE(mt9v111_formats) - 1) 8178c2ecf20Sopenharmony_ci return -EINVAL; 8188c2ecf20Sopenharmony_ci 8198c2ecf20Sopenharmony_ci code->code = mt9v111_formats[code->index].code; 8208c2ecf20Sopenharmony_ci 8218c2ecf20Sopenharmony_ci return 0; 8228c2ecf20Sopenharmony_ci} 8238c2ecf20Sopenharmony_ci 8248c2ecf20Sopenharmony_cistatic int mt9v111_enum_frame_interval(struct v4l2_subdev *sd, 8258c2ecf20Sopenharmony_ci struct v4l2_subdev_pad_config *cfg, 8268c2ecf20Sopenharmony_ci struct v4l2_subdev_frame_interval_enum *fie) 8278c2ecf20Sopenharmony_ci{ 8288c2ecf20Sopenharmony_ci unsigned int i; 8298c2ecf20Sopenharmony_ci 8308c2ecf20Sopenharmony_ci if (fie->pad || fie->index >= ARRAY_SIZE(mt9v111_frame_intervals)) 8318c2ecf20Sopenharmony_ci return -EINVAL; 8328c2ecf20Sopenharmony_ci 8338c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(mt9v111_frame_sizes); i++) 8348c2ecf20Sopenharmony_ci if (fie->width == mt9v111_frame_sizes[i].width && 8358c2ecf20Sopenharmony_ci fie->height == mt9v111_frame_sizes[i].height) 8368c2ecf20Sopenharmony_ci break; 8378c2ecf20Sopenharmony_ci 8388c2ecf20Sopenharmony_ci if (i == ARRAY_SIZE(mt9v111_frame_sizes)) 8398c2ecf20Sopenharmony_ci return -EINVAL; 8408c2ecf20Sopenharmony_ci 8418c2ecf20Sopenharmony_ci fie->interval.numerator = 1; 8428c2ecf20Sopenharmony_ci fie->interval.denominator = mt9v111_frame_intervals[fie->index]; 8438c2ecf20Sopenharmony_ci 8448c2ecf20Sopenharmony_ci return 0; 8458c2ecf20Sopenharmony_ci} 8468c2ecf20Sopenharmony_ci 8478c2ecf20Sopenharmony_cistatic int mt9v111_enum_frame_size(struct v4l2_subdev *subdev, 8488c2ecf20Sopenharmony_ci struct v4l2_subdev_pad_config *cfg, 8498c2ecf20Sopenharmony_ci struct v4l2_subdev_frame_size_enum *fse) 8508c2ecf20Sopenharmony_ci{ 8518c2ecf20Sopenharmony_ci if (fse->pad || fse->index >= ARRAY_SIZE(mt9v111_frame_sizes)) 8528c2ecf20Sopenharmony_ci return -EINVAL; 8538c2ecf20Sopenharmony_ci 8548c2ecf20Sopenharmony_ci fse->min_width = mt9v111_frame_sizes[fse->index].width; 8558c2ecf20Sopenharmony_ci fse->max_width = mt9v111_frame_sizes[fse->index].width; 8568c2ecf20Sopenharmony_ci fse->min_height = mt9v111_frame_sizes[fse->index].height; 8578c2ecf20Sopenharmony_ci fse->max_height = mt9v111_frame_sizes[fse->index].height; 8588c2ecf20Sopenharmony_ci 8598c2ecf20Sopenharmony_ci return 0; 8608c2ecf20Sopenharmony_ci} 8618c2ecf20Sopenharmony_ci 8628c2ecf20Sopenharmony_cistatic int mt9v111_get_format(struct v4l2_subdev *subdev, 8638c2ecf20Sopenharmony_ci struct v4l2_subdev_pad_config *cfg, 8648c2ecf20Sopenharmony_ci struct v4l2_subdev_format *format) 8658c2ecf20Sopenharmony_ci{ 8668c2ecf20Sopenharmony_ci struct mt9v111_dev *mt9v111 = sd_to_mt9v111(subdev); 8678c2ecf20Sopenharmony_ci 8688c2ecf20Sopenharmony_ci if (format->pad) 8698c2ecf20Sopenharmony_ci return -EINVAL; 8708c2ecf20Sopenharmony_ci 8718c2ecf20Sopenharmony_ci mutex_lock(&mt9v111->stream_mutex); 8728c2ecf20Sopenharmony_ci format->format = *__mt9v111_get_pad_format(mt9v111, cfg, format->pad, 8738c2ecf20Sopenharmony_ci format->which); 8748c2ecf20Sopenharmony_ci mutex_unlock(&mt9v111->stream_mutex); 8758c2ecf20Sopenharmony_ci 8768c2ecf20Sopenharmony_ci return 0; 8778c2ecf20Sopenharmony_ci} 8788c2ecf20Sopenharmony_ci 8798c2ecf20Sopenharmony_cistatic int mt9v111_set_format(struct v4l2_subdev *subdev, 8808c2ecf20Sopenharmony_ci struct v4l2_subdev_pad_config *cfg, 8818c2ecf20Sopenharmony_ci struct v4l2_subdev_format *format) 8828c2ecf20Sopenharmony_ci{ 8838c2ecf20Sopenharmony_ci struct mt9v111_dev *mt9v111 = sd_to_mt9v111(subdev); 8848c2ecf20Sopenharmony_ci struct v4l2_mbus_framefmt new_fmt; 8858c2ecf20Sopenharmony_ci struct v4l2_mbus_framefmt *__fmt; 8868c2ecf20Sopenharmony_ci unsigned int best_fit = ~0L; 8878c2ecf20Sopenharmony_ci unsigned int idx = 0; 8888c2ecf20Sopenharmony_ci unsigned int i; 8898c2ecf20Sopenharmony_ci 8908c2ecf20Sopenharmony_ci mutex_lock(&mt9v111->stream_mutex); 8918c2ecf20Sopenharmony_ci if (mt9v111->streaming) { 8928c2ecf20Sopenharmony_ci mutex_unlock(&mt9v111->stream_mutex); 8938c2ecf20Sopenharmony_ci return -EBUSY; 8948c2ecf20Sopenharmony_ci } 8958c2ecf20Sopenharmony_ci 8968c2ecf20Sopenharmony_ci if (format->pad) { 8978c2ecf20Sopenharmony_ci mutex_unlock(&mt9v111->stream_mutex); 8988c2ecf20Sopenharmony_ci return -EINVAL; 8998c2ecf20Sopenharmony_ci } 9008c2ecf20Sopenharmony_ci 9018c2ecf20Sopenharmony_ci /* Update mbus format code and sizes. */ 9028c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(mt9v111_formats); i++) { 9038c2ecf20Sopenharmony_ci if (format->format.code == mt9v111_formats[i].code) { 9048c2ecf20Sopenharmony_ci new_fmt.code = mt9v111_formats[i].code; 9058c2ecf20Sopenharmony_ci break; 9068c2ecf20Sopenharmony_ci } 9078c2ecf20Sopenharmony_ci } 9088c2ecf20Sopenharmony_ci if (i == ARRAY_SIZE(mt9v111_formats)) 9098c2ecf20Sopenharmony_ci new_fmt.code = mt9v111_formats[0].code; 9108c2ecf20Sopenharmony_ci 9118c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(mt9v111_frame_sizes); i++) { 9128c2ecf20Sopenharmony_ci unsigned int fit = abs(mt9v111_frame_sizes[i].width - 9138c2ecf20Sopenharmony_ci format->format.width) + 9148c2ecf20Sopenharmony_ci abs(mt9v111_frame_sizes[i].height - 9158c2ecf20Sopenharmony_ci format->format.height); 9168c2ecf20Sopenharmony_ci if (fit < best_fit) { 9178c2ecf20Sopenharmony_ci best_fit = fit; 9188c2ecf20Sopenharmony_ci idx = i; 9198c2ecf20Sopenharmony_ci 9208c2ecf20Sopenharmony_ci if (fit == 0) 9218c2ecf20Sopenharmony_ci break; 9228c2ecf20Sopenharmony_ci } 9238c2ecf20Sopenharmony_ci } 9248c2ecf20Sopenharmony_ci new_fmt.width = mt9v111_frame_sizes[idx].width; 9258c2ecf20Sopenharmony_ci new_fmt.height = mt9v111_frame_sizes[idx].height; 9268c2ecf20Sopenharmony_ci 9278c2ecf20Sopenharmony_ci /* Update the device (or pad) format if it has changed. */ 9288c2ecf20Sopenharmony_ci __fmt = __mt9v111_get_pad_format(mt9v111, cfg, format->pad, 9298c2ecf20Sopenharmony_ci format->which); 9308c2ecf20Sopenharmony_ci 9318c2ecf20Sopenharmony_ci /* Format hasn't changed, stop here. */ 9328c2ecf20Sopenharmony_ci if (__fmt->code == new_fmt.code && 9338c2ecf20Sopenharmony_ci __fmt->width == new_fmt.width && 9348c2ecf20Sopenharmony_ci __fmt->height == new_fmt.height) 9358c2ecf20Sopenharmony_ci goto done; 9368c2ecf20Sopenharmony_ci 9378c2ecf20Sopenharmony_ci /* Update the format and sizes, then mark changes as pending. */ 9388c2ecf20Sopenharmony_ci __fmt->code = new_fmt.code; 9398c2ecf20Sopenharmony_ci __fmt->width = new_fmt.width; 9408c2ecf20Sopenharmony_ci __fmt->height = new_fmt.height; 9418c2ecf20Sopenharmony_ci 9428c2ecf20Sopenharmony_ci if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) 9438c2ecf20Sopenharmony_ci mt9v111->pending = true; 9448c2ecf20Sopenharmony_ci 9458c2ecf20Sopenharmony_ci dev_dbg(mt9v111->dev, "%s: mbus_code: %x - (%ux%u)\n", 9468c2ecf20Sopenharmony_ci __func__, __fmt->code, __fmt->width, __fmt->height); 9478c2ecf20Sopenharmony_ci 9488c2ecf20Sopenharmony_cidone: 9498c2ecf20Sopenharmony_ci format->format = *__fmt; 9508c2ecf20Sopenharmony_ci 9518c2ecf20Sopenharmony_ci mutex_unlock(&mt9v111->stream_mutex); 9528c2ecf20Sopenharmony_ci 9538c2ecf20Sopenharmony_ci return 0; 9548c2ecf20Sopenharmony_ci} 9558c2ecf20Sopenharmony_ci 9568c2ecf20Sopenharmony_cistatic int mt9v111_init_cfg(struct v4l2_subdev *subdev, 9578c2ecf20Sopenharmony_ci struct v4l2_subdev_pad_config *cfg) 9588c2ecf20Sopenharmony_ci{ 9598c2ecf20Sopenharmony_ci cfg->try_fmt = mt9v111_def_fmt; 9608c2ecf20Sopenharmony_ci 9618c2ecf20Sopenharmony_ci return 0; 9628c2ecf20Sopenharmony_ci} 9638c2ecf20Sopenharmony_ci 9648c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_core_ops mt9v111_core_ops = { 9658c2ecf20Sopenharmony_ci .s_power = mt9v111_s_power, 9668c2ecf20Sopenharmony_ci}; 9678c2ecf20Sopenharmony_ci 9688c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_video_ops mt9v111_video_ops = { 9698c2ecf20Sopenharmony_ci .s_stream = mt9v111_s_stream, 9708c2ecf20Sopenharmony_ci .s_frame_interval = mt9v111_s_frame_interval, 9718c2ecf20Sopenharmony_ci .g_frame_interval = mt9v111_g_frame_interval, 9728c2ecf20Sopenharmony_ci}; 9738c2ecf20Sopenharmony_ci 9748c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_pad_ops mt9v111_pad_ops = { 9758c2ecf20Sopenharmony_ci .init_cfg = mt9v111_init_cfg, 9768c2ecf20Sopenharmony_ci .enum_mbus_code = mt9v111_enum_mbus_code, 9778c2ecf20Sopenharmony_ci .enum_frame_size = mt9v111_enum_frame_size, 9788c2ecf20Sopenharmony_ci .enum_frame_interval = mt9v111_enum_frame_interval, 9798c2ecf20Sopenharmony_ci .get_fmt = mt9v111_get_format, 9808c2ecf20Sopenharmony_ci .set_fmt = mt9v111_set_format, 9818c2ecf20Sopenharmony_ci}; 9828c2ecf20Sopenharmony_ci 9838c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_ops mt9v111_ops = { 9848c2ecf20Sopenharmony_ci .core = &mt9v111_core_ops, 9858c2ecf20Sopenharmony_ci .video = &mt9v111_video_ops, 9868c2ecf20Sopenharmony_ci .pad = &mt9v111_pad_ops, 9878c2ecf20Sopenharmony_ci}; 9888c2ecf20Sopenharmony_ci 9898c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_MEDIA_CONTROLLER) 9908c2ecf20Sopenharmony_cistatic const struct media_entity_operations mt9v111_subdev_entity_ops = { 9918c2ecf20Sopenharmony_ci .link_validate = v4l2_subdev_link_validate, 9928c2ecf20Sopenharmony_ci}; 9938c2ecf20Sopenharmony_ci#endif 9948c2ecf20Sopenharmony_ci 9958c2ecf20Sopenharmony_ci/* --- V4L2 ctrl --- */ 9968c2ecf20Sopenharmony_cistatic int mt9v111_s_ctrl(struct v4l2_ctrl *ctrl) 9978c2ecf20Sopenharmony_ci{ 9988c2ecf20Sopenharmony_ci struct mt9v111_dev *mt9v111 = container_of(ctrl->handler, 9998c2ecf20Sopenharmony_ci struct mt9v111_dev, 10008c2ecf20Sopenharmony_ci ctrls); 10018c2ecf20Sopenharmony_ci int ret; 10028c2ecf20Sopenharmony_ci 10038c2ecf20Sopenharmony_ci mutex_lock(&mt9v111->pwr_mutex); 10048c2ecf20Sopenharmony_ci /* 10058c2ecf20Sopenharmony_ci * If sensor is powered down, just cache new control values, 10068c2ecf20Sopenharmony_ci * no actual register access. 10078c2ecf20Sopenharmony_ci */ 10088c2ecf20Sopenharmony_ci if (!mt9v111->pwr_count) { 10098c2ecf20Sopenharmony_ci mt9v111->pending = true; 10108c2ecf20Sopenharmony_ci mutex_unlock(&mt9v111->pwr_mutex); 10118c2ecf20Sopenharmony_ci return 0; 10128c2ecf20Sopenharmony_ci } 10138c2ecf20Sopenharmony_ci mutex_unlock(&mt9v111->pwr_mutex); 10148c2ecf20Sopenharmony_ci 10158c2ecf20Sopenharmony_ci /* 10168c2ecf20Sopenharmony_ci * Flickering control gets disabled if both auto exp and auto awb 10178c2ecf20Sopenharmony_ci * are disabled too. If any of the two is enabled, enable it. 10188c2ecf20Sopenharmony_ci * 10198c2ecf20Sopenharmony_ci * Disabling flickering when ae and awb are off allows a more precise 10208c2ecf20Sopenharmony_ci * control of the programmed frame rate. 10218c2ecf20Sopenharmony_ci */ 10228c2ecf20Sopenharmony_ci if (mt9v111->auto_exp->is_new || mt9v111->auto_awb->is_new) { 10238c2ecf20Sopenharmony_ci if (mt9v111->auto_exp->val == V4L2_EXPOSURE_MANUAL && 10248c2ecf20Sopenharmony_ci mt9v111->auto_awb->val == V4L2_WHITE_BALANCE_MANUAL) 10258c2ecf20Sopenharmony_ci ret = mt9v111_update(mt9v111->client, MT9V111_R01_IFP, 10268c2ecf20Sopenharmony_ci MT9V111_IFP_R08_OUTFMT_CTRL, 10278c2ecf20Sopenharmony_ci MT9V111_IFP_R08_OUTFMT_CTRL_FLICKER, 10288c2ecf20Sopenharmony_ci 0); 10298c2ecf20Sopenharmony_ci else 10308c2ecf20Sopenharmony_ci ret = mt9v111_update(mt9v111->client, MT9V111_R01_IFP, 10318c2ecf20Sopenharmony_ci MT9V111_IFP_R08_OUTFMT_CTRL, 10328c2ecf20Sopenharmony_ci MT9V111_IFP_R08_OUTFMT_CTRL_FLICKER, 10338c2ecf20Sopenharmony_ci 1); 10348c2ecf20Sopenharmony_ci if (ret) 10358c2ecf20Sopenharmony_ci return ret; 10368c2ecf20Sopenharmony_ci } 10378c2ecf20Sopenharmony_ci 10388c2ecf20Sopenharmony_ci ret = -EINVAL; 10398c2ecf20Sopenharmony_ci switch (ctrl->id) { 10408c2ecf20Sopenharmony_ci case V4L2_CID_AUTO_WHITE_BALANCE: 10418c2ecf20Sopenharmony_ci ret = mt9v111_update(mt9v111->client, MT9V111_R01_IFP, 10428c2ecf20Sopenharmony_ci MT9V111_IFP_R06_OPMODE_CTRL, 10438c2ecf20Sopenharmony_ci MT9V111_IFP_R06_OPMODE_CTRL_AWB_EN, 10448c2ecf20Sopenharmony_ci ctrl->val == V4L2_WHITE_BALANCE_AUTO ? 10458c2ecf20Sopenharmony_ci MT9V111_IFP_R06_OPMODE_CTRL_AWB_EN : 0); 10468c2ecf20Sopenharmony_ci break; 10478c2ecf20Sopenharmony_ci case V4L2_CID_EXPOSURE_AUTO: 10488c2ecf20Sopenharmony_ci ret = mt9v111_update(mt9v111->client, MT9V111_R01_IFP, 10498c2ecf20Sopenharmony_ci MT9V111_IFP_R06_OPMODE_CTRL, 10508c2ecf20Sopenharmony_ci MT9V111_IFP_R06_OPMODE_CTRL_AE_EN, 10518c2ecf20Sopenharmony_ci ctrl->val == V4L2_EXPOSURE_AUTO ? 10528c2ecf20Sopenharmony_ci MT9V111_IFP_R06_OPMODE_CTRL_AE_EN : 0); 10538c2ecf20Sopenharmony_ci break; 10548c2ecf20Sopenharmony_ci case V4L2_CID_HBLANK: 10558c2ecf20Sopenharmony_ci ret = mt9v111_update(mt9v111->client, MT9V111_R01_CORE, 10568c2ecf20Sopenharmony_ci MT9V111_CORE_R05_HBLANK, 10578c2ecf20Sopenharmony_ci MT9V111_CORE_R05_MAX_HBLANK, 10588c2ecf20Sopenharmony_ci mt9v111->hblank->val); 10598c2ecf20Sopenharmony_ci break; 10608c2ecf20Sopenharmony_ci case V4L2_CID_VBLANK: 10618c2ecf20Sopenharmony_ci ret = mt9v111_update(mt9v111->client, MT9V111_R01_CORE, 10628c2ecf20Sopenharmony_ci MT9V111_CORE_R06_VBLANK, 10638c2ecf20Sopenharmony_ci MT9V111_CORE_R06_MAX_VBLANK, 10648c2ecf20Sopenharmony_ci mt9v111->vblank->val); 10658c2ecf20Sopenharmony_ci break; 10668c2ecf20Sopenharmony_ci } 10678c2ecf20Sopenharmony_ci 10688c2ecf20Sopenharmony_ci return ret; 10698c2ecf20Sopenharmony_ci} 10708c2ecf20Sopenharmony_ci 10718c2ecf20Sopenharmony_cistatic const struct v4l2_ctrl_ops mt9v111_ctrl_ops = { 10728c2ecf20Sopenharmony_ci .s_ctrl = mt9v111_s_ctrl, 10738c2ecf20Sopenharmony_ci}; 10748c2ecf20Sopenharmony_ci 10758c2ecf20Sopenharmony_cistatic int mt9v111_chip_probe(struct mt9v111_dev *mt9v111) 10768c2ecf20Sopenharmony_ci{ 10778c2ecf20Sopenharmony_ci int ret; 10788c2ecf20Sopenharmony_ci u16 val; 10798c2ecf20Sopenharmony_ci 10808c2ecf20Sopenharmony_ci ret = __mt9v111_power_on(&mt9v111->sd); 10818c2ecf20Sopenharmony_ci if (ret) 10828c2ecf20Sopenharmony_ci return ret; 10838c2ecf20Sopenharmony_ci 10848c2ecf20Sopenharmony_ci ret = mt9v111_read(mt9v111->client, MT9V111_R01_CORE, 10858c2ecf20Sopenharmony_ci MT9V111_CORE_RFF_CHIP_VER, &val); 10868c2ecf20Sopenharmony_ci if (ret) 10878c2ecf20Sopenharmony_ci goto power_off; 10888c2ecf20Sopenharmony_ci 10898c2ecf20Sopenharmony_ci if ((val >> 8) != MT9V111_CHIP_ID_HIGH && 10908c2ecf20Sopenharmony_ci (val & 0xff) != MT9V111_CHIP_ID_LOW) { 10918c2ecf20Sopenharmony_ci dev_err(mt9v111->dev, 10928c2ecf20Sopenharmony_ci "Unable to identify MT9V111 chip: 0x%2x%2x\n", 10938c2ecf20Sopenharmony_ci val >> 8, val & 0xff); 10948c2ecf20Sopenharmony_ci ret = -EIO; 10958c2ecf20Sopenharmony_ci goto power_off; 10968c2ecf20Sopenharmony_ci } 10978c2ecf20Sopenharmony_ci 10988c2ecf20Sopenharmony_ci dev_dbg(mt9v111->dev, "Chip identified: 0x%2x%2x\n", 10998c2ecf20Sopenharmony_ci val >> 8, val & 0xff); 11008c2ecf20Sopenharmony_ci 11018c2ecf20Sopenharmony_cipower_off: 11028c2ecf20Sopenharmony_ci __mt9v111_power_off(&mt9v111->sd); 11038c2ecf20Sopenharmony_ci 11048c2ecf20Sopenharmony_ci return ret; 11058c2ecf20Sopenharmony_ci} 11068c2ecf20Sopenharmony_ci 11078c2ecf20Sopenharmony_cistatic int mt9v111_probe(struct i2c_client *client) 11088c2ecf20Sopenharmony_ci{ 11098c2ecf20Sopenharmony_ci struct mt9v111_dev *mt9v111; 11108c2ecf20Sopenharmony_ci struct v4l2_fract tpf; 11118c2ecf20Sopenharmony_ci int ret; 11128c2ecf20Sopenharmony_ci 11138c2ecf20Sopenharmony_ci mt9v111 = devm_kzalloc(&client->dev, sizeof(*mt9v111), GFP_KERNEL); 11148c2ecf20Sopenharmony_ci if (!mt9v111) 11158c2ecf20Sopenharmony_ci return -ENOMEM; 11168c2ecf20Sopenharmony_ci 11178c2ecf20Sopenharmony_ci mt9v111->dev = &client->dev; 11188c2ecf20Sopenharmony_ci mt9v111->client = client; 11198c2ecf20Sopenharmony_ci 11208c2ecf20Sopenharmony_ci mt9v111->clk = devm_clk_get(&client->dev, NULL); 11218c2ecf20Sopenharmony_ci if (IS_ERR(mt9v111->clk)) 11228c2ecf20Sopenharmony_ci return PTR_ERR(mt9v111->clk); 11238c2ecf20Sopenharmony_ci 11248c2ecf20Sopenharmony_ci mt9v111->sysclk = clk_get_rate(mt9v111->clk); 11258c2ecf20Sopenharmony_ci if (mt9v111->sysclk > MT9V111_MAX_CLKIN) 11268c2ecf20Sopenharmony_ci return -EINVAL; 11278c2ecf20Sopenharmony_ci 11288c2ecf20Sopenharmony_ci mt9v111->oe = devm_gpiod_get_optional(&client->dev, "enable", 11298c2ecf20Sopenharmony_ci GPIOD_OUT_LOW); 11308c2ecf20Sopenharmony_ci if (IS_ERR(mt9v111->oe)) { 11318c2ecf20Sopenharmony_ci dev_err(&client->dev, "Unable to get GPIO \"enable\": %ld\n", 11328c2ecf20Sopenharmony_ci PTR_ERR(mt9v111->oe)); 11338c2ecf20Sopenharmony_ci return PTR_ERR(mt9v111->oe); 11348c2ecf20Sopenharmony_ci } 11358c2ecf20Sopenharmony_ci 11368c2ecf20Sopenharmony_ci mt9v111->standby = devm_gpiod_get_optional(&client->dev, "standby", 11378c2ecf20Sopenharmony_ci GPIOD_OUT_HIGH); 11388c2ecf20Sopenharmony_ci if (IS_ERR(mt9v111->standby)) { 11398c2ecf20Sopenharmony_ci dev_err(&client->dev, "Unable to get GPIO \"standby\": %ld\n", 11408c2ecf20Sopenharmony_ci PTR_ERR(mt9v111->standby)); 11418c2ecf20Sopenharmony_ci return PTR_ERR(mt9v111->standby); 11428c2ecf20Sopenharmony_ci } 11438c2ecf20Sopenharmony_ci 11448c2ecf20Sopenharmony_ci mt9v111->reset = devm_gpiod_get_optional(&client->dev, "reset", 11458c2ecf20Sopenharmony_ci GPIOD_OUT_LOW); 11468c2ecf20Sopenharmony_ci if (IS_ERR(mt9v111->reset)) { 11478c2ecf20Sopenharmony_ci dev_err(&client->dev, "Unable to get GPIO \"reset\": %ld\n", 11488c2ecf20Sopenharmony_ci PTR_ERR(mt9v111->reset)); 11498c2ecf20Sopenharmony_ci return PTR_ERR(mt9v111->reset); 11508c2ecf20Sopenharmony_ci } 11518c2ecf20Sopenharmony_ci 11528c2ecf20Sopenharmony_ci mutex_init(&mt9v111->pwr_mutex); 11538c2ecf20Sopenharmony_ci mutex_init(&mt9v111->stream_mutex); 11548c2ecf20Sopenharmony_ci 11558c2ecf20Sopenharmony_ci v4l2_ctrl_handler_init(&mt9v111->ctrls, 5); 11568c2ecf20Sopenharmony_ci 11578c2ecf20Sopenharmony_ci mt9v111->auto_awb = v4l2_ctrl_new_std(&mt9v111->ctrls, 11588c2ecf20Sopenharmony_ci &mt9v111_ctrl_ops, 11598c2ecf20Sopenharmony_ci V4L2_CID_AUTO_WHITE_BALANCE, 11608c2ecf20Sopenharmony_ci 0, 1, 1, 11618c2ecf20Sopenharmony_ci V4L2_WHITE_BALANCE_AUTO); 11628c2ecf20Sopenharmony_ci mt9v111->auto_exp = v4l2_ctrl_new_std_menu(&mt9v111->ctrls, 11638c2ecf20Sopenharmony_ci &mt9v111_ctrl_ops, 11648c2ecf20Sopenharmony_ci V4L2_CID_EXPOSURE_AUTO, 11658c2ecf20Sopenharmony_ci V4L2_EXPOSURE_MANUAL, 11668c2ecf20Sopenharmony_ci 0, V4L2_EXPOSURE_AUTO); 11678c2ecf20Sopenharmony_ci mt9v111->hblank = v4l2_ctrl_new_std(&mt9v111->ctrls, &mt9v111_ctrl_ops, 11688c2ecf20Sopenharmony_ci V4L2_CID_HBLANK, 11698c2ecf20Sopenharmony_ci MT9V111_CORE_R05_MIN_HBLANK, 11708c2ecf20Sopenharmony_ci MT9V111_CORE_R05_MAX_HBLANK, 1, 11718c2ecf20Sopenharmony_ci MT9V111_CORE_R05_DEF_HBLANK); 11728c2ecf20Sopenharmony_ci mt9v111->vblank = v4l2_ctrl_new_std(&mt9v111->ctrls, &mt9v111_ctrl_ops, 11738c2ecf20Sopenharmony_ci V4L2_CID_VBLANK, 11748c2ecf20Sopenharmony_ci MT9V111_CORE_R06_MIN_VBLANK, 11758c2ecf20Sopenharmony_ci MT9V111_CORE_R06_MAX_VBLANK, 1, 11768c2ecf20Sopenharmony_ci MT9V111_CORE_R06_DEF_VBLANK); 11778c2ecf20Sopenharmony_ci 11788c2ecf20Sopenharmony_ci /* PIXEL_RATE is fixed: just expose it to user space. */ 11798c2ecf20Sopenharmony_ci v4l2_ctrl_new_std(&mt9v111->ctrls, &mt9v111_ctrl_ops, 11808c2ecf20Sopenharmony_ci V4L2_CID_PIXEL_RATE, 0, 11818c2ecf20Sopenharmony_ci DIV_ROUND_CLOSEST(mt9v111->sysclk, 2), 1, 11828c2ecf20Sopenharmony_ci DIV_ROUND_CLOSEST(mt9v111->sysclk, 2)); 11838c2ecf20Sopenharmony_ci 11848c2ecf20Sopenharmony_ci if (mt9v111->ctrls.error) { 11858c2ecf20Sopenharmony_ci ret = mt9v111->ctrls.error; 11868c2ecf20Sopenharmony_ci goto error_free_ctrls; 11878c2ecf20Sopenharmony_ci } 11888c2ecf20Sopenharmony_ci mt9v111->sd.ctrl_handler = &mt9v111->ctrls; 11898c2ecf20Sopenharmony_ci 11908c2ecf20Sopenharmony_ci /* Start with default configuration: 640x480 UYVY. */ 11918c2ecf20Sopenharmony_ci mt9v111->fmt = mt9v111_def_fmt; 11928c2ecf20Sopenharmony_ci 11938c2ecf20Sopenharmony_ci /* Re-calculate blankings for 640x480@15fps. */ 11948c2ecf20Sopenharmony_ci mt9v111->fps = 15; 11958c2ecf20Sopenharmony_ci tpf.numerator = 1; 11968c2ecf20Sopenharmony_ci tpf.denominator = mt9v111->fps; 11978c2ecf20Sopenharmony_ci mt9v111_calc_frame_rate(mt9v111, &tpf); 11988c2ecf20Sopenharmony_ci 11998c2ecf20Sopenharmony_ci mt9v111->pwr_count = 0; 12008c2ecf20Sopenharmony_ci mt9v111->addr_space = MT9V111_R01_IFP; 12018c2ecf20Sopenharmony_ci mt9v111->pending = true; 12028c2ecf20Sopenharmony_ci 12038c2ecf20Sopenharmony_ci v4l2_i2c_subdev_init(&mt9v111->sd, client, &mt9v111_ops); 12048c2ecf20Sopenharmony_ci 12058c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_MEDIA_CONTROLLER) 12068c2ecf20Sopenharmony_ci mt9v111->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; 12078c2ecf20Sopenharmony_ci mt9v111->sd.entity.ops = &mt9v111_subdev_entity_ops; 12088c2ecf20Sopenharmony_ci mt9v111->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; 12098c2ecf20Sopenharmony_ci 12108c2ecf20Sopenharmony_ci mt9v111->pad.flags = MEDIA_PAD_FL_SOURCE; 12118c2ecf20Sopenharmony_ci ret = media_entity_pads_init(&mt9v111->sd.entity, 1, &mt9v111->pad); 12128c2ecf20Sopenharmony_ci if (ret) 12138c2ecf20Sopenharmony_ci goto error_free_entity; 12148c2ecf20Sopenharmony_ci#endif 12158c2ecf20Sopenharmony_ci 12168c2ecf20Sopenharmony_ci ret = mt9v111_chip_probe(mt9v111); 12178c2ecf20Sopenharmony_ci if (ret) 12188c2ecf20Sopenharmony_ci goto error_free_entity; 12198c2ecf20Sopenharmony_ci 12208c2ecf20Sopenharmony_ci ret = v4l2_async_register_subdev(&mt9v111->sd); 12218c2ecf20Sopenharmony_ci if (ret) 12228c2ecf20Sopenharmony_ci goto error_free_entity; 12238c2ecf20Sopenharmony_ci 12248c2ecf20Sopenharmony_ci return 0; 12258c2ecf20Sopenharmony_ci 12268c2ecf20Sopenharmony_cierror_free_entity: 12278c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_MEDIA_CONTROLLER) 12288c2ecf20Sopenharmony_ci media_entity_cleanup(&mt9v111->sd.entity); 12298c2ecf20Sopenharmony_ci#endif 12308c2ecf20Sopenharmony_ci 12318c2ecf20Sopenharmony_cierror_free_ctrls: 12328c2ecf20Sopenharmony_ci v4l2_ctrl_handler_free(&mt9v111->ctrls); 12338c2ecf20Sopenharmony_ci 12348c2ecf20Sopenharmony_ci mutex_destroy(&mt9v111->pwr_mutex); 12358c2ecf20Sopenharmony_ci mutex_destroy(&mt9v111->stream_mutex); 12368c2ecf20Sopenharmony_ci 12378c2ecf20Sopenharmony_ci return ret; 12388c2ecf20Sopenharmony_ci} 12398c2ecf20Sopenharmony_ci 12408c2ecf20Sopenharmony_cistatic int mt9v111_remove(struct i2c_client *client) 12418c2ecf20Sopenharmony_ci{ 12428c2ecf20Sopenharmony_ci struct v4l2_subdev *sd = i2c_get_clientdata(client); 12438c2ecf20Sopenharmony_ci struct mt9v111_dev *mt9v111 = sd_to_mt9v111(sd); 12448c2ecf20Sopenharmony_ci 12458c2ecf20Sopenharmony_ci v4l2_async_unregister_subdev(sd); 12468c2ecf20Sopenharmony_ci 12478c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_MEDIA_CONTROLLER) 12488c2ecf20Sopenharmony_ci media_entity_cleanup(&sd->entity); 12498c2ecf20Sopenharmony_ci#endif 12508c2ecf20Sopenharmony_ci 12518c2ecf20Sopenharmony_ci v4l2_ctrl_handler_free(&mt9v111->ctrls); 12528c2ecf20Sopenharmony_ci 12538c2ecf20Sopenharmony_ci mutex_destroy(&mt9v111->pwr_mutex); 12548c2ecf20Sopenharmony_ci mutex_destroy(&mt9v111->stream_mutex); 12558c2ecf20Sopenharmony_ci 12568c2ecf20Sopenharmony_ci devm_gpiod_put(mt9v111->dev, mt9v111->oe); 12578c2ecf20Sopenharmony_ci devm_gpiod_put(mt9v111->dev, mt9v111->standby); 12588c2ecf20Sopenharmony_ci devm_gpiod_put(mt9v111->dev, mt9v111->reset); 12598c2ecf20Sopenharmony_ci 12608c2ecf20Sopenharmony_ci devm_clk_put(mt9v111->dev, mt9v111->clk); 12618c2ecf20Sopenharmony_ci 12628c2ecf20Sopenharmony_ci return 0; 12638c2ecf20Sopenharmony_ci} 12648c2ecf20Sopenharmony_ci 12658c2ecf20Sopenharmony_cistatic const struct of_device_id mt9v111_of_match[] = { 12668c2ecf20Sopenharmony_ci { .compatible = "aptina,mt9v111", }, 12678c2ecf20Sopenharmony_ci { /* sentinel */ }, 12688c2ecf20Sopenharmony_ci}; 12698c2ecf20Sopenharmony_ci 12708c2ecf20Sopenharmony_cistatic struct i2c_driver mt9v111_driver = { 12718c2ecf20Sopenharmony_ci .driver = { 12728c2ecf20Sopenharmony_ci .name = "mt9v111", 12738c2ecf20Sopenharmony_ci .of_match_table = mt9v111_of_match, 12748c2ecf20Sopenharmony_ci }, 12758c2ecf20Sopenharmony_ci .probe_new = mt9v111_probe, 12768c2ecf20Sopenharmony_ci .remove = mt9v111_remove, 12778c2ecf20Sopenharmony_ci}; 12788c2ecf20Sopenharmony_ci 12798c2ecf20Sopenharmony_cimodule_i2c_driver(mt9v111_driver); 12808c2ecf20Sopenharmony_ci 12818c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("V4L2 sensor driver for Aptina MT9V111"); 12828c2ecf20Sopenharmony_ciMODULE_AUTHOR("Jacopo Mondi <jacopo@jmondi.org>"); 12838c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 1284