18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Driver for RJ54N1CB0C CMOS Image Sensor from Sharp 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2018, Jacopo Mondi <jacopo@jmondi.org> 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Copyright (C) 2009, Guennadi Liakhovetski <g.liakhovetski@gmx.de> 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/clk.h> 118c2ecf20Sopenharmony_ci#include <linux/delay.h> 128c2ecf20Sopenharmony_ci#include <linux/gpio/consumer.h> 138c2ecf20Sopenharmony_ci#include <linux/i2c.h> 148c2ecf20Sopenharmony_ci#include <linux/module.h> 158c2ecf20Sopenharmony_ci#include <linux/slab.h> 168c2ecf20Sopenharmony_ci#include <linux/v4l2-mediabus.h> 178c2ecf20Sopenharmony_ci#include <linux/videodev2.h> 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#include <media/i2c/rj54n1cb0c.h> 208c2ecf20Sopenharmony_ci#include <media/v4l2-device.h> 218c2ecf20Sopenharmony_ci#include <media/v4l2-ctrls.h> 228c2ecf20Sopenharmony_ci#include <media/v4l2-subdev.h> 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#define RJ54N1_DEV_CODE 0x0400 258c2ecf20Sopenharmony_ci#define RJ54N1_DEV_CODE2 0x0401 268c2ecf20Sopenharmony_ci#define RJ54N1_OUT_SEL 0x0403 278c2ecf20Sopenharmony_ci#define RJ54N1_XY_OUTPUT_SIZE_S_H 0x0404 288c2ecf20Sopenharmony_ci#define RJ54N1_X_OUTPUT_SIZE_S_L 0x0405 298c2ecf20Sopenharmony_ci#define RJ54N1_Y_OUTPUT_SIZE_S_L 0x0406 308c2ecf20Sopenharmony_ci#define RJ54N1_XY_OUTPUT_SIZE_P_H 0x0407 318c2ecf20Sopenharmony_ci#define RJ54N1_X_OUTPUT_SIZE_P_L 0x0408 328c2ecf20Sopenharmony_ci#define RJ54N1_Y_OUTPUT_SIZE_P_L 0x0409 338c2ecf20Sopenharmony_ci#define RJ54N1_LINE_LENGTH_PCK_S_H 0x040a 348c2ecf20Sopenharmony_ci#define RJ54N1_LINE_LENGTH_PCK_S_L 0x040b 358c2ecf20Sopenharmony_ci#define RJ54N1_LINE_LENGTH_PCK_P_H 0x040c 368c2ecf20Sopenharmony_ci#define RJ54N1_LINE_LENGTH_PCK_P_L 0x040d 378c2ecf20Sopenharmony_ci#define RJ54N1_RESIZE_N 0x040e 388c2ecf20Sopenharmony_ci#define RJ54N1_RESIZE_N_STEP 0x040f 398c2ecf20Sopenharmony_ci#define RJ54N1_RESIZE_STEP 0x0410 408c2ecf20Sopenharmony_ci#define RJ54N1_RESIZE_HOLD_H 0x0411 418c2ecf20Sopenharmony_ci#define RJ54N1_RESIZE_HOLD_L 0x0412 428c2ecf20Sopenharmony_ci#define RJ54N1_H_OBEN_OFS 0x0413 438c2ecf20Sopenharmony_ci#define RJ54N1_V_OBEN_OFS 0x0414 448c2ecf20Sopenharmony_ci#define RJ54N1_RESIZE_CONTROL 0x0415 458c2ecf20Sopenharmony_ci#define RJ54N1_STILL_CONTROL 0x0417 468c2ecf20Sopenharmony_ci#define RJ54N1_INC_USE_SEL_H 0x0425 478c2ecf20Sopenharmony_ci#define RJ54N1_INC_USE_SEL_L 0x0426 488c2ecf20Sopenharmony_ci#define RJ54N1_MIRROR_STILL_MODE 0x0427 498c2ecf20Sopenharmony_ci#define RJ54N1_INIT_START 0x0428 508c2ecf20Sopenharmony_ci#define RJ54N1_SCALE_1_2_LEV 0x0429 518c2ecf20Sopenharmony_ci#define RJ54N1_SCALE_4_LEV 0x042a 528c2ecf20Sopenharmony_ci#define RJ54N1_Y_GAIN 0x04d8 538c2ecf20Sopenharmony_ci#define RJ54N1_APT_GAIN_UP 0x04fa 548c2ecf20Sopenharmony_ci#define RJ54N1_RA_SEL_UL 0x0530 558c2ecf20Sopenharmony_ci#define RJ54N1_BYTE_SWAP 0x0531 568c2ecf20Sopenharmony_ci#define RJ54N1_OUT_SIGPO 0x053b 578c2ecf20Sopenharmony_ci#define RJ54N1_WB_SEL_WEIGHT_I 0x054e 588c2ecf20Sopenharmony_ci#define RJ54N1_BIT8_WB 0x0569 598c2ecf20Sopenharmony_ci#define RJ54N1_HCAPS_WB 0x056a 608c2ecf20Sopenharmony_ci#define RJ54N1_VCAPS_WB 0x056b 618c2ecf20Sopenharmony_ci#define RJ54N1_HCAPE_WB 0x056c 628c2ecf20Sopenharmony_ci#define RJ54N1_VCAPE_WB 0x056d 638c2ecf20Sopenharmony_ci#define RJ54N1_EXPOSURE_CONTROL 0x058c 648c2ecf20Sopenharmony_ci#define RJ54N1_FRAME_LENGTH_S_H 0x0595 658c2ecf20Sopenharmony_ci#define RJ54N1_FRAME_LENGTH_S_L 0x0596 668c2ecf20Sopenharmony_ci#define RJ54N1_FRAME_LENGTH_P_H 0x0597 678c2ecf20Sopenharmony_ci#define RJ54N1_FRAME_LENGTH_P_L 0x0598 688c2ecf20Sopenharmony_ci#define RJ54N1_PEAK_H 0x05b7 698c2ecf20Sopenharmony_ci#define RJ54N1_PEAK_50 0x05b8 708c2ecf20Sopenharmony_ci#define RJ54N1_PEAK_60 0x05b9 718c2ecf20Sopenharmony_ci#define RJ54N1_PEAK_DIFF 0x05ba 728c2ecf20Sopenharmony_ci#define RJ54N1_IOC 0x05ef 738c2ecf20Sopenharmony_ci#define RJ54N1_TG_BYPASS 0x0700 748c2ecf20Sopenharmony_ci#define RJ54N1_PLL_L 0x0701 758c2ecf20Sopenharmony_ci#define RJ54N1_PLL_N 0x0702 768c2ecf20Sopenharmony_ci#define RJ54N1_PLL_EN 0x0704 778c2ecf20Sopenharmony_ci#define RJ54N1_RATIO_TG 0x0706 788c2ecf20Sopenharmony_ci#define RJ54N1_RATIO_T 0x0707 798c2ecf20Sopenharmony_ci#define RJ54N1_RATIO_R 0x0708 808c2ecf20Sopenharmony_ci#define RJ54N1_RAMP_TGCLK_EN 0x0709 818c2ecf20Sopenharmony_ci#define RJ54N1_OCLK_DSP 0x0710 828c2ecf20Sopenharmony_ci#define RJ54N1_RATIO_OP 0x0711 838c2ecf20Sopenharmony_ci#define RJ54N1_RATIO_O 0x0712 848c2ecf20Sopenharmony_ci#define RJ54N1_OCLK_SEL_EN 0x0713 858c2ecf20Sopenharmony_ci#define RJ54N1_CLK_RST 0x0717 868c2ecf20Sopenharmony_ci#define RJ54N1_RESET_STANDBY 0x0718 878c2ecf20Sopenharmony_ci#define RJ54N1_FWFLG 0x07fe 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci#define E_EXCLK (1 << 7) 908c2ecf20Sopenharmony_ci#define SOFT_STDBY (1 << 4) 918c2ecf20Sopenharmony_ci#define SEN_RSTX (1 << 2) 928c2ecf20Sopenharmony_ci#define TG_RSTX (1 << 1) 938c2ecf20Sopenharmony_ci#define DSP_RSTX (1 << 0) 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci#define RESIZE_HOLD_SEL (1 << 2) 968c2ecf20Sopenharmony_ci#define RESIZE_GO (1 << 1) 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci/* 998c2ecf20Sopenharmony_ci * When cropping, the camera automatically centers the cropped region, there 1008c2ecf20Sopenharmony_ci * doesn't seem to be a way to specify an explicit location of the rectangle. 1018c2ecf20Sopenharmony_ci */ 1028c2ecf20Sopenharmony_ci#define RJ54N1_COLUMN_SKIP 0 1038c2ecf20Sopenharmony_ci#define RJ54N1_ROW_SKIP 0 1048c2ecf20Sopenharmony_ci#define RJ54N1_MAX_WIDTH 1600 1058c2ecf20Sopenharmony_ci#define RJ54N1_MAX_HEIGHT 1200 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci#define PLL_L 2 1088c2ecf20Sopenharmony_ci#define PLL_N 0x31 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci/* I2C addresses: 0x50, 0x51, 0x60, 0x61 */ 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci/* RJ54N1CB0C has only one fixed colorspace per pixelcode */ 1138c2ecf20Sopenharmony_cistruct rj54n1_datafmt { 1148c2ecf20Sopenharmony_ci u32 code; 1158c2ecf20Sopenharmony_ci enum v4l2_colorspace colorspace; 1168c2ecf20Sopenharmony_ci}; 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci/* Find a data format by a pixel code in an array */ 1198c2ecf20Sopenharmony_cistatic const struct rj54n1_datafmt *rj54n1_find_datafmt( 1208c2ecf20Sopenharmony_ci u32 code, const struct rj54n1_datafmt *fmt, 1218c2ecf20Sopenharmony_ci int n) 1228c2ecf20Sopenharmony_ci{ 1238c2ecf20Sopenharmony_ci int i; 1248c2ecf20Sopenharmony_ci for (i = 0; i < n; i++) 1258c2ecf20Sopenharmony_ci if (fmt[i].code == code) 1268c2ecf20Sopenharmony_ci return fmt + i; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci return NULL; 1298c2ecf20Sopenharmony_ci} 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_cistatic const struct rj54n1_datafmt rj54n1_colour_fmts[] = { 1328c2ecf20Sopenharmony_ci {MEDIA_BUS_FMT_YUYV8_2X8, V4L2_COLORSPACE_JPEG}, 1338c2ecf20Sopenharmony_ci {MEDIA_BUS_FMT_YVYU8_2X8, V4L2_COLORSPACE_JPEG}, 1348c2ecf20Sopenharmony_ci {MEDIA_BUS_FMT_RGB565_2X8_LE, V4L2_COLORSPACE_SRGB}, 1358c2ecf20Sopenharmony_ci {MEDIA_BUS_FMT_RGB565_2X8_BE, V4L2_COLORSPACE_SRGB}, 1368c2ecf20Sopenharmony_ci {MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE, V4L2_COLORSPACE_SRGB}, 1378c2ecf20Sopenharmony_ci {MEDIA_BUS_FMT_SBGGR10_2X8_PADLO_LE, V4L2_COLORSPACE_SRGB}, 1388c2ecf20Sopenharmony_ci {MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_BE, V4L2_COLORSPACE_SRGB}, 1398c2ecf20Sopenharmony_ci {MEDIA_BUS_FMT_SBGGR10_2X8_PADLO_BE, V4L2_COLORSPACE_SRGB}, 1408c2ecf20Sopenharmony_ci {MEDIA_BUS_FMT_SBGGR10_1X10, V4L2_COLORSPACE_SRGB}, 1418c2ecf20Sopenharmony_ci}; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_cistruct rj54n1_clock_div { 1448c2ecf20Sopenharmony_ci u8 ratio_tg; /* can be 0 or an odd number */ 1458c2ecf20Sopenharmony_ci u8 ratio_t; 1468c2ecf20Sopenharmony_ci u8 ratio_r; 1478c2ecf20Sopenharmony_ci u8 ratio_op; 1488c2ecf20Sopenharmony_ci u8 ratio_o; 1498c2ecf20Sopenharmony_ci}; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_cistruct rj54n1 { 1528c2ecf20Sopenharmony_ci struct v4l2_subdev subdev; 1538c2ecf20Sopenharmony_ci struct v4l2_ctrl_handler hdl; 1548c2ecf20Sopenharmony_ci struct clk *clk; 1558c2ecf20Sopenharmony_ci struct gpio_desc *pwup_gpio; 1568c2ecf20Sopenharmony_ci struct gpio_desc *enable_gpio; 1578c2ecf20Sopenharmony_ci struct rj54n1_clock_div clk_div; 1588c2ecf20Sopenharmony_ci const struct rj54n1_datafmt *fmt; 1598c2ecf20Sopenharmony_ci struct v4l2_rect rect; /* Sensor window */ 1608c2ecf20Sopenharmony_ci unsigned int tgclk_mhz; 1618c2ecf20Sopenharmony_ci bool auto_wb; 1628c2ecf20Sopenharmony_ci unsigned short width; /* Output window */ 1638c2ecf20Sopenharmony_ci unsigned short height; 1648c2ecf20Sopenharmony_ci unsigned short resize; /* Sensor * 1024 / resize = Output */ 1658c2ecf20Sopenharmony_ci unsigned short scale; 1668c2ecf20Sopenharmony_ci u8 bank; 1678c2ecf20Sopenharmony_ci}; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_cistruct rj54n1_reg_val { 1708c2ecf20Sopenharmony_ci u16 reg; 1718c2ecf20Sopenharmony_ci u8 val; 1728c2ecf20Sopenharmony_ci}; 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_cistatic const struct rj54n1_reg_val bank_4[] = { 1758c2ecf20Sopenharmony_ci {0x417, 0}, 1768c2ecf20Sopenharmony_ci {0x42c, 0}, 1778c2ecf20Sopenharmony_ci {0x42d, 0xf0}, 1788c2ecf20Sopenharmony_ci {0x42e, 0}, 1798c2ecf20Sopenharmony_ci {0x42f, 0x50}, 1808c2ecf20Sopenharmony_ci {0x430, 0xf5}, 1818c2ecf20Sopenharmony_ci {0x431, 0x16}, 1828c2ecf20Sopenharmony_ci {0x432, 0x20}, 1838c2ecf20Sopenharmony_ci {0x433, 0}, 1848c2ecf20Sopenharmony_ci {0x434, 0xc8}, 1858c2ecf20Sopenharmony_ci {0x43c, 8}, 1868c2ecf20Sopenharmony_ci {0x43e, 0x90}, 1878c2ecf20Sopenharmony_ci {0x445, 0x83}, 1888c2ecf20Sopenharmony_ci {0x4ba, 0x58}, 1898c2ecf20Sopenharmony_ci {0x4bb, 4}, 1908c2ecf20Sopenharmony_ci {0x4bc, 0x20}, 1918c2ecf20Sopenharmony_ci {0x4db, 4}, 1928c2ecf20Sopenharmony_ci {0x4fe, 2}, 1938c2ecf20Sopenharmony_ci}; 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_cistatic const struct rj54n1_reg_val bank_5[] = { 1968c2ecf20Sopenharmony_ci {0x514, 0}, 1978c2ecf20Sopenharmony_ci {0x516, 0}, 1988c2ecf20Sopenharmony_ci {0x518, 0}, 1998c2ecf20Sopenharmony_ci {0x51a, 0}, 2008c2ecf20Sopenharmony_ci {0x51d, 0xff}, 2018c2ecf20Sopenharmony_ci {0x56f, 0x28}, 2028c2ecf20Sopenharmony_ci {0x575, 0x40}, 2038c2ecf20Sopenharmony_ci {0x5bc, 0x48}, 2048c2ecf20Sopenharmony_ci {0x5c1, 6}, 2058c2ecf20Sopenharmony_ci {0x5e5, 0x11}, 2068c2ecf20Sopenharmony_ci {0x5e6, 0x43}, 2078c2ecf20Sopenharmony_ci {0x5e7, 0x33}, 2088c2ecf20Sopenharmony_ci {0x5e8, 0x21}, 2098c2ecf20Sopenharmony_ci {0x5e9, 0x30}, 2108c2ecf20Sopenharmony_ci {0x5ea, 0x0}, 2118c2ecf20Sopenharmony_ci {0x5eb, 0xa5}, 2128c2ecf20Sopenharmony_ci {0x5ec, 0xff}, 2138c2ecf20Sopenharmony_ci {0x5fe, 2}, 2148c2ecf20Sopenharmony_ci}; 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_cistatic const struct rj54n1_reg_val bank_7[] = { 2178c2ecf20Sopenharmony_ci {0x70a, 0}, 2188c2ecf20Sopenharmony_ci {0x714, 0xff}, 2198c2ecf20Sopenharmony_ci {0x715, 0xff}, 2208c2ecf20Sopenharmony_ci {0x716, 0x1f}, 2218c2ecf20Sopenharmony_ci {0x7FE, 2}, 2228c2ecf20Sopenharmony_ci}; 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_cistatic const struct rj54n1_reg_val bank_8[] = { 2258c2ecf20Sopenharmony_ci {0x800, 0x00}, 2268c2ecf20Sopenharmony_ci {0x801, 0x01}, 2278c2ecf20Sopenharmony_ci {0x802, 0x61}, 2288c2ecf20Sopenharmony_ci {0x805, 0x00}, 2298c2ecf20Sopenharmony_ci {0x806, 0x00}, 2308c2ecf20Sopenharmony_ci {0x807, 0x00}, 2318c2ecf20Sopenharmony_ci {0x808, 0x00}, 2328c2ecf20Sopenharmony_ci {0x809, 0x01}, 2338c2ecf20Sopenharmony_ci {0x80A, 0x61}, 2348c2ecf20Sopenharmony_ci {0x80B, 0x00}, 2358c2ecf20Sopenharmony_ci {0x80C, 0x01}, 2368c2ecf20Sopenharmony_ci {0x80D, 0x00}, 2378c2ecf20Sopenharmony_ci {0x80E, 0x00}, 2388c2ecf20Sopenharmony_ci {0x80F, 0x00}, 2398c2ecf20Sopenharmony_ci {0x810, 0x00}, 2408c2ecf20Sopenharmony_ci {0x811, 0x01}, 2418c2ecf20Sopenharmony_ci {0x812, 0x61}, 2428c2ecf20Sopenharmony_ci {0x813, 0x00}, 2438c2ecf20Sopenharmony_ci {0x814, 0x11}, 2448c2ecf20Sopenharmony_ci {0x815, 0x00}, 2458c2ecf20Sopenharmony_ci {0x816, 0x41}, 2468c2ecf20Sopenharmony_ci {0x817, 0x00}, 2478c2ecf20Sopenharmony_ci {0x818, 0x51}, 2488c2ecf20Sopenharmony_ci {0x819, 0x01}, 2498c2ecf20Sopenharmony_ci {0x81A, 0x1F}, 2508c2ecf20Sopenharmony_ci {0x81B, 0x00}, 2518c2ecf20Sopenharmony_ci {0x81C, 0x01}, 2528c2ecf20Sopenharmony_ci {0x81D, 0x00}, 2538c2ecf20Sopenharmony_ci {0x81E, 0x11}, 2548c2ecf20Sopenharmony_ci {0x81F, 0x00}, 2558c2ecf20Sopenharmony_ci {0x820, 0x41}, 2568c2ecf20Sopenharmony_ci {0x821, 0x00}, 2578c2ecf20Sopenharmony_ci {0x822, 0x51}, 2588c2ecf20Sopenharmony_ci {0x823, 0x00}, 2598c2ecf20Sopenharmony_ci {0x824, 0x00}, 2608c2ecf20Sopenharmony_ci {0x825, 0x00}, 2618c2ecf20Sopenharmony_ci {0x826, 0x47}, 2628c2ecf20Sopenharmony_ci {0x827, 0x01}, 2638c2ecf20Sopenharmony_ci {0x828, 0x4F}, 2648c2ecf20Sopenharmony_ci {0x829, 0x00}, 2658c2ecf20Sopenharmony_ci {0x82A, 0x00}, 2668c2ecf20Sopenharmony_ci {0x82B, 0x00}, 2678c2ecf20Sopenharmony_ci {0x82C, 0x30}, 2688c2ecf20Sopenharmony_ci {0x82D, 0x00}, 2698c2ecf20Sopenharmony_ci {0x82E, 0x40}, 2708c2ecf20Sopenharmony_ci {0x82F, 0x00}, 2718c2ecf20Sopenharmony_ci {0x830, 0xB3}, 2728c2ecf20Sopenharmony_ci {0x831, 0x00}, 2738c2ecf20Sopenharmony_ci {0x832, 0xE3}, 2748c2ecf20Sopenharmony_ci {0x833, 0x00}, 2758c2ecf20Sopenharmony_ci {0x834, 0x00}, 2768c2ecf20Sopenharmony_ci {0x835, 0x00}, 2778c2ecf20Sopenharmony_ci {0x836, 0x00}, 2788c2ecf20Sopenharmony_ci {0x837, 0x00}, 2798c2ecf20Sopenharmony_ci {0x838, 0x00}, 2808c2ecf20Sopenharmony_ci {0x839, 0x01}, 2818c2ecf20Sopenharmony_ci {0x83A, 0x61}, 2828c2ecf20Sopenharmony_ci {0x83B, 0x00}, 2838c2ecf20Sopenharmony_ci {0x83C, 0x01}, 2848c2ecf20Sopenharmony_ci {0x83D, 0x00}, 2858c2ecf20Sopenharmony_ci {0x83E, 0x00}, 2868c2ecf20Sopenharmony_ci {0x83F, 0x00}, 2878c2ecf20Sopenharmony_ci {0x840, 0x00}, 2888c2ecf20Sopenharmony_ci {0x841, 0x01}, 2898c2ecf20Sopenharmony_ci {0x842, 0x61}, 2908c2ecf20Sopenharmony_ci {0x843, 0x00}, 2918c2ecf20Sopenharmony_ci {0x844, 0x1D}, 2928c2ecf20Sopenharmony_ci {0x845, 0x00}, 2938c2ecf20Sopenharmony_ci {0x846, 0x00}, 2948c2ecf20Sopenharmony_ci {0x847, 0x00}, 2958c2ecf20Sopenharmony_ci {0x848, 0x00}, 2968c2ecf20Sopenharmony_ci {0x849, 0x01}, 2978c2ecf20Sopenharmony_ci {0x84A, 0x1F}, 2988c2ecf20Sopenharmony_ci {0x84B, 0x00}, 2998c2ecf20Sopenharmony_ci {0x84C, 0x05}, 3008c2ecf20Sopenharmony_ci {0x84D, 0x00}, 3018c2ecf20Sopenharmony_ci {0x84E, 0x19}, 3028c2ecf20Sopenharmony_ci {0x84F, 0x01}, 3038c2ecf20Sopenharmony_ci {0x850, 0x21}, 3048c2ecf20Sopenharmony_ci {0x851, 0x01}, 3058c2ecf20Sopenharmony_ci {0x852, 0x5D}, 3068c2ecf20Sopenharmony_ci {0x853, 0x00}, 3078c2ecf20Sopenharmony_ci {0x854, 0x00}, 3088c2ecf20Sopenharmony_ci {0x855, 0x00}, 3098c2ecf20Sopenharmony_ci {0x856, 0x19}, 3108c2ecf20Sopenharmony_ci {0x857, 0x01}, 3118c2ecf20Sopenharmony_ci {0x858, 0x21}, 3128c2ecf20Sopenharmony_ci {0x859, 0x00}, 3138c2ecf20Sopenharmony_ci {0x85A, 0x00}, 3148c2ecf20Sopenharmony_ci {0x85B, 0x00}, 3158c2ecf20Sopenharmony_ci {0x85C, 0x00}, 3168c2ecf20Sopenharmony_ci {0x85D, 0x00}, 3178c2ecf20Sopenharmony_ci {0x85E, 0x00}, 3188c2ecf20Sopenharmony_ci {0x85F, 0x00}, 3198c2ecf20Sopenharmony_ci {0x860, 0xB3}, 3208c2ecf20Sopenharmony_ci {0x861, 0x00}, 3218c2ecf20Sopenharmony_ci {0x862, 0xE3}, 3228c2ecf20Sopenharmony_ci {0x863, 0x00}, 3238c2ecf20Sopenharmony_ci {0x864, 0x00}, 3248c2ecf20Sopenharmony_ci {0x865, 0x00}, 3258c2ecf20Sopenharmony_ci {0x866, 0x00}, 3268c2ecf20Sopenharmony_ci {0x867, 0x00}, 3278c2ecf20Sopenharmony_ci {0x868, 0x00}, 3288c2ecf20Sopenharmony_ci {0x869, 0xE2}, 3298c2ecf20Sopenharmony_ci {0x86A, 0x00}, 3308c2ecf20Sopenharmony_ci {0x86B, 0x01}, 3318c2ecf20Sopenharmony_ci {0x86C, 0x06}, 3328c2ecf20Sopenharmony_ci {0x86D, 0x00}, 3338c2ecf20Sopenharmony_ci {0x86E, 0x00}, 3348c2ecf20Sopenharmony_ci {0x86F, 0x00}, 3358c2ecf20Sopenharmony_ci {0x870, 0x60}, 3368c2ecf20Sopenharmony_ci {0x871, 0x8C}, 3378c2ecf20Sopenharmony_ci {0x872, 0x10}, 3388c2ecf20Sopenharmony_ci {0x873, 0x00}, 3398c2ecf20Sopenharmony_ci {0x874, 0xE0}, 3408c2ecf20Sopenharmony_ci {0x875, 0x00}, 3418c2ecf20Sopenharmony_ci {0x876, 0x27}, 3428c2ecf20Sopenharmony_ci {0x877, 0x01}, 3438c2ecf20Sopenharmony_ci {0x878, 0x00}, 3448c2ecf20Sopenharmony_ci {0x879, 0x00}, 3458c2ecf20Sopenharmony_ci {0x87A, 0x00}, 3468c2ecf20Sopenharmony_ci {0x87B, 0x03}, 3478c2ecf20Sopenharmony_ci {0x87C, 0x00}, 3488c2ecf20Sopenharmony_ci {0x87D, 0x00}, 3498c2ecf20Sopenharmony_ci {0x87E, 0x00}, 3508c2ecf20Sopenharmony_ci {0x87F, 0x00}, 3518c2ecf20Sopenharmony_ci {0x880, 0x00}, 3528c2ecf20Sopenharmony_ci {0x881, 0x00}, 3538c2ecf20Sopenharmony_ci {0x882, 0x00}, 3548c2ecf20Sopenharmony_ci {0x883, 0x00}, 3558c2ecf20Sopenharmony_ci {0x884, 0x00}, 3568c2ecf20Sopenharmony_ci {0x885, 0x00}, 3578c2ecf20Sopenharmony_ci {0x886, 0xF8}, 3588c2ecf20Sopenharmony_ci {0x887, 0x00}, 3598c2ecf20Sopenharmony_ci {0x888, 0x03}, 3608c2ecf20Sopenharmony_ci {0x889, 0x00}, 3618c2ecf20Sopenharmony_ci {0x88A, 0x64}, 3628c2ecf20Sopenharmony_ci {0x88B, 0x00}, 3638c2ecf20Sopenharmony_ci {0x88C, 0x03}, 3648c2ecf20Sopenharmony_ci {0x88D, 0x00}, 3658c2ecf20Sopenharmony_ci {0x88E, 0xB1}, 3668c2ecf20Sopenharmony_ci {0x88F, 0x00}, 3678c2ecf20Sopenharmony_ci {0x890, 0x03}, 3688c2ecf20Sopenharmony_ci {0x891, 0x01}, 3698c2ecf20Sopenharmony_ci {0x892, 0x1D}, 3708c2ecf20Sopenharmony_ci {0x893, 0x00}, 3718c2ecf20Sopenharmony_ci {0x894, 0x03}, 3728c2ecf20Sopenharmony_ci {0x895, 0x01}, 3738c2ecf20Sopenharmony_ci {0x896, 0x4B}, 3748c2ecf20Sopenharmony_ci {0x897, 0x00}, 3758c2ecf20Sopenharmony_ci {0x898, 0xE5}, 3768c2ecf20Sopenharmony_ci {0x899, 0x00}, 3778c2ecf20Sopenharmony_ci {0x89A, 0x01}, 3788c2ecf20Sopenharmony_ci {0x89B, 0x00}, 3798c2ecf20Sopenharmony_ci {0x89C, 0x01}, 3808c2ecf20Sopenharmony_ci {0x89D, 0x04}, 3818c2ecf20Sopenharmony_ci {0x89E, 0xC8}, 3828c2ecf20Sopenharmony_ci {0x89F, 0x00}, 3838c2ecf20Sopenharmony_ci {0x8A0, 0x01}, 3848c2ecf20Sopenharmony_ci {0x8A1, 0x01}, 3858c2ecf20Sopenharmony_ci {0x8A2, 0x61}, 3868c2ecf20Sopenharmony_ci {0x8A3, 0x00}, 3878c2ecf20Sopenharmony_ci {0x8A4, 0x01}, 3888c2ecf20Sopenharmony_ci {0x8A5, 0x00}, 3898c2ecf20Sopenharmony_ci {0x8A6, 0x00}, 3908c2ecf20Sopenharmony_ci {0x8A7, 0x00}, 3918c2ecf20Sopenharmony_ci {0x8A8, 0x00}, 3928c2ecf20Sopenharmony_ci {0x8A9, 0x00}, 3938c2ecf20Sopenharmony_ci {0x8AA, 0x7F}, 3948c2ecf20Sopenharmony_ci {0x8AB, 0x03}, 3958c2ecf20Sopenharmony_ci {0x8AC, 0x00}, 3968c2ecf20Sopenharmony_ci {0x8AD, 0x00}, 3978c2ecf20Sopenharmony_ci {0x8AE, 0x00}, 3988c2ecf20Sopenharmony_ci {0x8AF, 0x00}, 3998c2ecf20Sopenharmony_ci {0x8B0, 0x00}, 4008c2ecf20Sopenharmony_ci {0x8B1, 0x00}, 4018c2ecf20Sopenharmony_ci {0x8B6, 0x00}, 4028c2ecf20Sopenharmony_ci {0x8B7, 0x01}, 4038c2ecf20Sopenharmony_ci {0x8B8, 0x00}, 4048c2ecf20Sopenharmony_ci {0x8B9, 0x00}, 4058c2ecf20Sopenharmony_ci {0x8BA, 0x02}, 4068c2ecf20Sopenharmony_ci {0x8BB, 0x00}, 4078c2ecf20Sopenharmony_ci {0x8BC, 0xFF}, 4088c2ecf20Sopenharmony_ci {0x8BD, 0x00}, 4098c2ecf20Sopenharmony_ci {0x8FE, 2}, 4108c2ecf20Sopenharmony_ci}; 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_cistatic const struct rj54n1_reg_val bank_10[] = { 4138c2ecf20Sopenharmony_ci {0x10bf, 0x69} 4148c2ecf20Sopenharmony_ci}; 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci/* Clock dividers - these are default register values, divider = register + 1 */ 4178c2ecf20Sopenharmony_cistatic const struct rj54n1_clock_div clk_div = { 4188c2ecf20Sopenharmony_ci .ratio_tg = 3 /* default: 5 */, 4198c2ecf20Sopenharmony_ci .ratio_t = 4 /* default: 1 */, 4208c2ecf20Sopenharmony_ci .ratio_r = 4 /* default: 0 */, 4218c2ecf20Sopenharmony_ci .ratio_op = 1 /* default: 5 */, 4228c2ecf20Sopenharmony_ci .ratio_o = 9 /* default: 0 */, 4238c2ecf20Sopenharmony_ci}; 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_cistatic struct rj54n1 *to_rj54n1(const struct i2c_client *client) 4268c2ecf20Sopenharmony_ci{ 4278c2ecf20Sopenharmony_ci return container_of(i2c_get_clientdata(client), struct rj54n1, subdev); 4288c2ecf20Sopenharmony_ci} 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_cistatic int reg_read(struct i2c_client *client, const u16 reg) 4318c2ecf20Sopenharmony_ci{ 4328c2ecf20Sopenharmony_ci struct rj54n1 *rj54n1 = to_rj54n1(client); 4338c2ecf20Sopenharmony_ci int ret; 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci /* set bank */ 4368c2ecf20Sopenharmony_ci if (rj54n1->bank != reg >> 8) { 4378c2ecf20Sopenharmony_ci dev_dbg(&client->dev, "[0x%x] = 0x%x\n", 0xff, reg >> 8); 4388c2ecf20Sopenharmony_ci ret = i2c_smbus_write_byte_data(client, 0xff, reg >> 8); 4398c2ecf20Sopenharmony_ci if (ret < 0) 4408c2ecf20Sopenharmony_ci return ret; 4418c2ecf20Sopenharmony_ci rj54n1->bank = reg >> 8; 4428c2ecf20Sopenharmony_ci } 4438c2ecf20Sopenharmony_ci return i2c_smbus_read_byte_data(client, reg & 0xff); 4448c2ecf20Sopenharmony_ci} 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_cistatic int reg_write(struct i2c_client *client, const u16 reg, 4478c2ecf20Sopenharmony_ci const u8 data) 4488c2ecf20Sopenharmony_ci{ 4498c2ecf20Sopenharmony_ci struct rj54n1 *rj54n1 = to_rj54n1(client); 4508c2ecf20Sopenharmony_ci int ret; 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci /* set bank */ 4538c2ecf20Sopenharmony_ci if (rj54n1->bank != reg >> 8) { 4548c2ecf20Sopenharmony_ci dev_dbg(&client->dev, "[0x%x] = 0x%x\n", 0xff, reg >> 8); 4558c2ecf20Sopenharmony_ci ret = i2c_smbus_write_byte_data(client, 0xff, reg >> 8); 4568c2ecf20Sopenharmony_ci if (ret < 0) 4578c2ecf20Sopenharmony_ci return ret; 4588c2ecf20Sopenharmony_ci rj54n1->bank = reg >> 8; 4598c2ecf20Sopenharmony_ci } 4608c2ecf20Sopenharmony_ci dev_dbg(&client->dev, "[0x%x] = 0x%x\n", reg & 0xff, data); 4618c2ecf20Sopenharmony_ci return i2c_smbus_write_byte_data(client, reg & 0xff, data); 4628c2ecf20Sopenharmony_ci} 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_cistatic int reg_set(struct i2c_client *client, const u16 reg, 4658c2ecf20Sopenharmony_ci const u8 data, const u8 mask) 4668c2ecf20Sopenharmony_ci{ 4678c2ecf20Sopenharmony_ci int ret; 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci ret = reg_read(client, reg); 4708c2ecf20Sopenharmony_ci if (ret < 0) 4718c2ecf20Sopenharmony_ci return ret; 4728c2ecf20Sopenharmony_ci return reg_write(client, reg, (ret & ~mask) | (data & mask)); 4738c2ecf20Sopenharmony_ci} 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_cistatic int reg_write_multiple(struct i2c_client *client, 4768c2ecf20Sopenharmony_ci const struct rj54n1_reg_val *rv, const int n) 4778c2ecf20Sopenharmony_ci{ 4788c2ecf20Sopenharmony_ci int i, ret; 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci for (i = 0; i < n; i++) { 4818c2ecf20Sopenharmony_ci ret = reg_write(client, rv->reg, rv->val); 4828c2ecf20Sopenharmony_ci if (ret < 0) 4838c2ecf20Sopenharmony_ci return ret; 4848c2ecf20Sopenharmony_ci rv++; 4858c2ecf20Sopenharmony_ci } 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci return 0; 4888c2ecf20Sopenharmony_ci} 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_cistatic int rj54n1_enum_mbus_code(struct v4l2_subdev *sd, 4918c2ecf20Sopenharmony_ci struct v4l2_subdev_pad_config *cfg, 4928c2ecf20Sopenharmony_ci struct v4l2_subdev_mbus_code_enum *code) 4938c2ecf20Sopenharmony_ci{ 4948c2ecf20Sopenharmony_ci if (code->pad || code->index >= ARRAY_SIZE(rj54n1_colour_fmts)) 4958c2ecf20Sopenharmony_ci return -EINVAL; 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci code->code = rj54n1_colour_fmts[code->index].code; 4988c2ecf20Sopenharmony_ci return 0; 4998c2ecf20Sopenharmony_ci} 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_cistatic int rj54n1_s_stream(struct v4l2_subdev *sd, int enable) 5028c2ecf20Sopenharmony_ci{ 5038c2ecf20Sopenharmony_ci struct i2c_client *client = v4l2_get_subdevdata(sd); 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci /* Switch between preview and still shot modes */ 5068c2ecf20Sopenharmony_ci return reg_set(client, RJ54N1_STILL_CONTROL, (!enable) << 7, 0x80); 5078c2ecf20Sopenharmony_ci} 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_cistatic int rj54n1_set_rect(struct i2c_client *client, 5108c2ecf20Sopenharmony_ci u16 reg_x, u16 reg_y, u16 reg_xy, 5118c2ecf20Sopenharmony_ci u32 width, u32 height) 5128c2ecf20Sopenharmony_ci{ 5138c2ecf20Sopenharmony_ci int ret; 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci ret = reg_write(client, reg_xy, 5168c2ecf20Sopenharmony_ci ((width >> 4) & 0x70) | 5178c2ecf20Sopenharmony_ci ((height >> 8) & 7)); 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci if (!ret) 5208c2ecf20Sopenharmony_ci ret = reg_write(client, reg_x, width & 0xff); 5218c2ecf20Sopenharmony_ci if (!ret) 5228c2ecf20Sopenharmony_ci ret = reg_write(client, reg_y, height & 0xff); 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci return ret; 5258c2ecf20Sopenharmony_ci} 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ci/* 5288c2ecf20Sopenharmony_ci * Some commands, specifically certain initialisation sequences, require 5298c2ecf20Sopenharmony_ci * a commit operation. 5308c2ecf20Sopenharmony_ci */ 5318c2ecf20Sopenharmony_cistatic int rj54n1_commit(struct i2c_client *client) 5328c2ecf20Sopenharmony_ci{ 5338c2ecf20Sopenharmony_ci int ret = reg_write(client, RJ54N1_INIT_START, 1); 5348c2ecf20Sopenharmony_ci msleep(10); 5358c2ecf20Sopenharmony_ci if (!ret) 5368c2ecf20Sopenharmony_ci ret = reg_write(client, RJ54N1_INIT_START, 0); 5378c2ecf20Sopenharmony_ci return ret; 5388c2ecf20Sopenharmony_ci} 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_cistatic int rj54n1_sensor_scale(struct v4l2_subdev *sd, s32 *in_w, s32 *in_h, 5418c2ecf20Sopenharmony_ci s32 *out_w, s32 *out_h); 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_cistatic int rj54n1_set_selection(struct v4l2_subdev *sd, 5448c2ecf20Sopenharmony_ci struct v4l2_subdev_pad_config *cfg, 5458c2ecf20Sopenharmony_ci struct v4l2_subdev_selection *sel) 5468c2ecf20Sopenharmony_ci{ 5478c2ecf20Sopenharmony_ci struct i2c_client *client = v4l2_get_subdevdata(sd); 5488c2ecf20Sopenharmony_ci struct rj54n1 *rj54n1 = to_rj54n1(client); 5498c2ecf20Sopenharmony_ci const struct v4l2_rect *rect = &sel->r; 5508c2ecf20Sopenharmony_ci int output_w, output_h, input_w = rect->width, input_h = rect->height; 5518c2ecf20Sopenharmony_ci int ret; 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ci if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE || 5548c2ecf20Sopenharmony_ci sel->target != V4L2_SEL_TGT_CROP) 5558c2ecf20Sopenharmony_ci return -EINVAL; 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_ci /* arbitrary minimum width and height, edges unimportant */ 5588c2ecf20Sopenharmony_ci v4l_bound_align_image(&input_w, 8, RJ54N1_MAX_WIDTH, 0, 5598c2ecf20Sopenharmony_ci &input_h, 8, RJ54N1_MAX_HEIGHT, 0, 0); 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci output_w = (input_w * 1024 + rj54n1->resize / 2) / rj54n1->resize; 5628c2ecf20Sopenharmony_ci output_h = (input_h * 1024 + rj54n1->resize / 2) / rj54n1->resize; 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ci dev_dbg(&client->dev, "Scaling for %dx%d : %u = %dx%d\n", 5658c2ecf20Sopenharmony_ci input_w, input_h, rj54n1->resize, output_w, output_h); 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_ci ret = rj54n1_sensor_scale(sd, &input_w, &input_h, &output_w, &output_h); 5688c2ecf20Sopenharmony_ci if (ret < 0) 5698c2ecf20Sopenharmony_ci return ret; 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_ci rj54n1->width = output_w; 5728c2ecf20Sopenharmony_ci rj54n1->height = output_h; 5738c2ecf20Sopenharmony_ci rj54n1->resize = ret; 5748c2ecf20Sopenharmony_ci rj54n1->rect.width = input_w; 5758c2ecf20Sopenharmony_ci rj54n1->rect.height = input_h; 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_ci return 0; 5788c2ecf20Sopenharmony_ci} 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_cistatic int rj54n1_get_selection(struct v4l2_subdev *sd, 5818c2ecf20Sopenharmony_ci struct v4l2_subdev_pad_config *cfg, 5828c2ecf20Sopenharmony_ci struct v4l2_subdev_selection *sel) 5838c2ecf20Sopenharmony_ci{ 5848c2ecf20Sopenharmony_ci struct i2c_client *client = v4l2_get_subdevdata(sd); 5858c2ecf20Sopenharmony_ci struct rj54n1 *rj54n1 = to_rj54n1(client); 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ci if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE) 5888c2ecf20Sopenharmony_ci return -EINVAL; 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_ci switch (sel->target) { 5918c2ecf20Sopenharmony_ci case V4L2_SEL_TGT_CROP_BOUNDS: 5928c2ecf20Sopenharmony_ci sel->r.left = RJ54N1_COLUMN_SKIP; 5938c2ecf20Sopenharmony_ci sel->r.top = RJ54N1_ROW_SKIP; 5948c2ecf20Sopenharmony_ci sel->r.width = RJ54N1_MAX_WIDTH; 5958c2ecf20Sopenharmony_ci sel->r.height = RJ54N1_MAX_HEIGHT; 5968c2ecf20Sopenharmony_ci return 0; 5978c2ecf20Sopenharmony_ci case V4L2_SEL_TGT_CROP: 5988c2ecf20Sopenharmony_ci sel->r = rj54n1->rect; 5998c2ecf20Sopenharmony_ci return 0; 6008c2ecf20Sopenharmony_ci default: 6018c2ecf20Sopenharmony_ci return -EINVAL; 6028c2ecf20Sopenharmony_ci } 6038c2ecf20Sopenharmony_ci} 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_cistatic int rj54n1_get_fmt(struct v4l2_subdev *sd, 6068c2ecf20Sopenharmony_ci struct v4l2_subdev_pad_config *cfg, 6078c2ecf20Sopenharmony_ci struct v4l2_subdev_format *format) 6088c2ecf20Sopenharmony_ci{ 6098c2ecf20Sopenharmony_ci struct v4l2_mbus_framefmt *mf = &format->format; 6108c2ecf20Sopenharmony_ci struct i2c_client *client = v4l2_get_subdevdata(sd); 6118c2ecf20Sopenharmony_ci struct rj54n1 *rj54n1 = to_rj54n1(client); 6128c2ecf20Sopenharmony_ci 6138c2ecf20Sopenharmony_ci if (format->pad) 6148c2ecf20Sopenharmony_ci return -EINVAL; 6158c2ecf20Sopenharmony_ci 6168c2ecf20Sopenharmony_ci mf->code = rj54n1->fmt->code; 6178c2ecf20Sopenharmony_ci mf->colorspace = rj54n1->fmt->colorspace; 6188c2ecf20Sopenharmony_ci mf->ycbcr_enc = V4L2_YCBCR_ENC_601; 6198c2ecf20Sopenharmony_ci mf->xfer_func = V4L2_XFER_FUNC_SRGB; 6208c2ecf20Sopenharmony_ci mf->quantization = V4L2_QUANTIZATION_DEFAULT; 6218c2ecf20Sopenharmony_ci mf->field = V4L2_FIELD_NONE; 6228c2ecf20Sopenharmony_ci mf->width = rj54n1->width; 6238c2ecf20Sopenharmony_ci mf->height = rj54n1->height; 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_ci return 0; 6268c2ecf20Sopenharmony_ci} 6278c2ecf20Sopenharmony_ci 6288c2ecf20Sopenharmony_ci/* 6298c2ecf20Sopenharmony_ci * The actual geometry configuration routine. It scales the input window into 6308c2ecf20Sopenharmony_ci * the output one, updates the window sizes and returns an error or the resize 6318c2ecf20Sopenharmony_ci * coefficient on success. Note: we only use the "Fixed Scaling" on this camera. 6328c2ecf20Sopenharmony_ci */ 6338c2ecf20Sopenharmony_cistatic int rj54n1_sensor_scale(struct v4l2_subdev *sd, s32 *in_w, s32 *in_h, 6348c2ecf20Sopenharmony_ci s32 *out_w, s32 *out_h) 6358c2ecf20Sopenharmony_ci{ 6368c2ecf20Sopenharmony_ci struct i2c_client *client = v4l2_get_subdevdata(sd); 6378c2ecf20Sopenharmony_ci struct rj54n1 *rj54n1 = to_rj54n1(client); 6388c2ecf20Sopenharmony_ci unsigned int skip, resize, input_w = *in_w, input_h = *in_h, 6398c2ecf20Sopenharmony_ci output_w = *out_w, output_h = *out_h; 6408c2ecf20Sopenharmony_ci u16 inc_sel, wb_bit8, wb_left, wb_right, wb_top, wb_bottom; 6418c2ecf20Sopenharmony_ci unsigned int peak, peak_50, peak_60; 6428c2ecf20Sopenharmony_ci int ret; 6438c2ecf20Sopenharmony_ci 6448c2ecf20Sopenharmony_ci /* 6458c2ecf20Sopenharmony_ci * We have a problem with crops, where the window is larger than 512x384 6468c2ecf20Sopenharmony_ci * and output window is larger than a half of the input one. In this 6478c2ecf20Sopenharmony_ci * case we have to either reduce the input window to equal or below 6488c2ecf20Sopenharmony_ci * 512x384 or the output window to equal or below 1/2 of the input. 6498c2ecf20Sopenharmony_ci */ 6508c2ecf20Sopenharmony_ci if (output_w > max(512U, input_w / 2)) { 6518c2ecf20Sopenharmony_ci if (2 * output_w > RJ54N1_MAX_WIDTH) { 6528c2ecf20Sopenharmony_ci input_w = RJ54N1_MAX_WIDTH; 6538c2ecf20Sopenharmony_ci output_w = RJ54N1_MAX_WIDTH / 2; 6548c2ecf20Sopenharmony_ci } else { 6558c2ecf20Sopenharmony_ci input_w = output_w * 2; 6568c2ecf20Sopenharmony_ci } 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_ci dev_dbg(&client->dev, "Adjusted output width: in %u, out %u\n", 6598c2ecf20Sopenharmony_ci input_w, output_w); 6608c2ecf20Sopenharmony_ci } 6618c2ecf20Sopenharmony_ci 6628c2ecf20Sopenharmony_ci if (output_h > max(384U, input_h / 2)) { 6638c2ecf20Sopenharmony_ci if (2 * output_h > RJ54N1_MAX_HEIGHT) { 6648c2ecf20Sopenharmony_ci input_h = RJ54N1_MAX_HEIGHT; 6658c2ecf20Sopenharmony_ci output_h = RJ54N1_MAX_HEIGHT / 2; 6668c2ecf20Sopenharmony_ci } else { 6678c2ecf20Sopenharmony_ci input_h = output_h * 2; 6688c2ecf20Sopenharmony_ci } 6698c2ecf20Sopenharmony_ci 6708c2ecf20Sopenharmony_ci dev_dbg(&client->dev, "Adjusted output height: in %u, out %u\n", 6718c2ecf20Sopenharmony_ci input_h, output_h); 6728c2ecf20Sopenharmony_ci } 6738c2ecf20Sopenharmony_ci 6748c2ecf20Sopenharmony_ci /* Idea: use the read mode for snapshots, handle separate geometries */ 6758c2ecf20Sopenharmony_ci ret = rj54n1_set_rect(client, RJ54N1_X_OUTPUT_SIZE_S_L, 6768c2ecf20Sopenharmony_ci RJ54N1_Y_OUTPUT_SIZE_S_L, 6778c2ecf20Sopenharmony_ci RJ54N1_XY_OUTPUT_SIZE_S_H, output_w, output_h); 6788c2ecf20Sopenharmony_ci if (!ret) 6798c2ecf20Sopenharmony_ci ret = rj54n1_set_rect(client, RJ54N1_X_OUTPUT_SIZE_P_L, 6808c2ecf20Sopenharmony_ci RJ54N1_Y_OUTPUT_SIZE_P_L, 6818c2ecf20Sopenharmony_ci RJ54N1_XY_OUTPUT_SIZE_P_H, output_w, output_h); 6828c2ecf20Sopenharmony_ci 6838c2ecf20Sopenharmony_ci if (ret < 0) 6848c2ecf20Sopenharmony_ci return ret; 6858c2ecf20Sopenharmony_ci 6868c2ecf20Sopenharmony_ci if (output_w > input_w && output_h > input_h) { 6878c2ecf20Sopenharmony_ci input_w = output_w; 6888c2ecf20Sopenharmony_ci input_h = output_h; 6898c2ecf20Sopenharmony_ci 6908c2ecf20Sopenharmony_ci resize = 1024; 6918c2ecf20Sopenharmony_ci } else { 6928c2ecf20Sopenharmony_ci unsigned int resize_x, resize_y; 6938c2ecf20Sopenharmony_ci resize_x = (input_w * 1024 + output_w / 2) / output_w; 6948c2ecf20Sopenharmony_ci resize_y = (input_h * 1024 + output_h / 2) / output_h; 6958c2ecf20Sopenharmony_ci 6968c2ecf20Sopenharmony_ci /* We want max(resize_x, resize_y), check if it still fits */ 6978c2ecf20Sopenharmony_ci if (resize_x > resize_y && 6988c2ecf20Sopenharmony_ci (output_h * resize_x + 512) / 1024 > RJ54N1_MAX_HEIGHT) 6998c2ecf20Sopenharmony_ci resize = (RJ54N1_MAX_HEIGHT * 1024 + output_h / 2) / 7008c2ecf20Sopenharmony_ci output_h; 7018c2ecf20Sopenharmony_ci else if (resize_y > resize_x && 7028c2ecf20Sopenharmony_ci (output_w * resize_y + 512) / 1024 > RJ54N1_MAX_WIDTH) 7038c2ecf20Sopenharmony_ci resize = (RJ54N1_MAX_WIDTH * 1024 + output_w / 2) / 7048c2ecf20Sopenharmony_ci output_w; 7058c2ecf20Sopenharmony_ci else 7068c2ecf20Sopenharmony_ci resize = max(resize_x, resize_y); 7078c2ecf20Sopenharmony_ci 7088c2ecf20Sopenharmony_ci /* Prohibited value ranges */ 7098c2ecf20Sopenharmony_ci switch (resize) { 7108c2ecf20Sopenharmony_ci case 2040 ... 2047: 7118c2ecf20Sopenharmony_ci resize = 2039; 7128c2ecf20Sopenharmony_ci break; 7138c2ecf20Sopenharmony_ci case 4080 ... 4095: 7148c2ecf20Sopenharmony_ci resize = 4079; 7158c2ecf20Sopenharmony_ci break; 7168c2ecf20Sopenharmony_ci case 8160 ... 8191: 7178c2ecf20Sopenharmony_ci resize = 8159; 7188c2ecf20Sopenharmony_ci break; 7198c2ecf20Sopenharmony_ci case 16320 ... 16384: 7208c2ecf20Sopenharmony_ci resize = 16319; 7218c2ecf20Sopenharmony_ci } 7228c2ecf20Sopenharmony_ci } 7238c2ecf20Sopenharmony_ci 7248c2ecf20Sopenharmony_ci /* Set scaling */ 7258c2ecf20Sopenharmony_ci ret = reg_write(client, RJ54N1_RESIZE_HOLD_L, resize & 0xff); 7268c2ecf20Sopenharmony_ci if (!ret) 7278c2ecf20Sopenharmony_ci ret = reg_write(client, RJ54N1_RESIZE_HOLD_H, resize >> 8); 7288c2ecf20Sopenharmony_ci 7298c2ecf20Sopenharmony_ci if (ret < 0) 7308c2ecf20Sopenharmony_ci return ret; 7318c2ecf20Sopenharmony_ci 7328c2ecf20Sopenharmony_ci /* 7338c2ecf20Sopenharmony_ci * Configure a skipping bitmask. The sensor will select a skipping value 7348c2ecf20Sopenharmony_ci * among set bits automatically. This is very unclear in the datasheet 7358c2ecf20Sopenharmony_ci * too. I was told, in this register one enables all skipping values, 7368c2ecf20Sopenharmony_ci * that are required for a specific resize, and the camera selects 7378c2ecf20Sopenharmony_ci * automatically, which ones to use. But it is unclear how to identify, 7388c2ecf20Sopenharmony_ci * which cropping values are needed. Secondly, why don't we just set all 7398c2ecf20Sopenharmony_ci * bits and let the camera choose? Would it increase processing time and 7408c2ecf20Sopenharmony_ci * reduce the framerate? Using 0xfffc for INC_USE_SEL doesn't seem to 7418c2ecf20Sopenharmony_ci * improve the image quality or stability for larger frames (see comment 7428c2ecf20Sopenharmony_ci * above), but I didn't check the framerate. 7438c2ecf20Sopenharmony_ci */ 7448c2ecf20Sopenharmony_ci skip = min(resize / 1024, 15U); 7458c2ecf20Sopenharmony_ci 7468c2ecf20Sopenharmony_ci inc_sel = 1 << skip; 7478c2ecf20Sopenharmony_ci 7488c2ecf20Sopenharmony_ci if (inc_sel <= 2) 7498c2ecf20Sopenharmony_ci inc_sel = 0xc; 7508c2ecf20Sopenharmony_ci else if (resize & 1023 && skip < 15) 7518c2ecf20Sopenharmony_ci inc_sel |= 1 << (skip + 1); 7528c2ecf20Sopenharmony_ci 7538c2ecf20Sopenharmony_ci ret = reg_write(client, RJ54N1_INC_USE_SEL_L, inc_sel & 0xfc); 7548c2ecf20Sopenharmony_ci if (!ret) 7558c2ecf20Sopenharmony_ci ret = reg_write(client, RJ54N1_INC_USE_SEL_H, inc_sel >> 8); 7568c2ecf20Sopenharmony_ci 7578c2ecf20Sopenharmony_ci if (!rj54n1->auto_wb) { 7588c2ecf20Sopenharmony_ci /* Auto white balance window */ 7598c2ecf20Sopenharmony_ci wb_left = output_w / 16; 7608c2ecf20Sopenharmony_ci wb_right = (3 * output_w / 4 - 3) / 4; 7618c2ecf20Sopenharmony_ci wb_top = output_h / 16; 7628c2ecf20Sopenharmony_ci wb_bottom = (3 * output_h / 4 - 3) / 4; 7638c2ecf20Sopenharmony_ci wb_bit8 = ((wb_left >> 2) & 0x40) | ((wb_top >> 4) & 0x10) | 7648c2ecf20Sopenharmony_ci ((wb_right >> 6) & 4) | ((wb_bottom >> 8) & 1); 7658c2ecf20Sopenharmony_ci 7668c2ecf20Sopenharmony_ci if (!ret) 7678c2ecf20Sopenharmony_ci ret = reg_write(client, RJ54N1_BIT8_WB, wb_bit8); 7688c2ecf20Sopenharmony_ci if (!ret) 7698c2ecf20Sopenharmony_ci ret = reg_write(client, RJ54N1_HCAPS_WB, wb_left); 7708c2ecf20Sopenharmony_ci if (!ret) 7718c2ecf20Sopenharmony_ci ret = reg_write(client, RJ54N1_VCAPS_WB, wb_top); 7728c2ecf20Sopenharmony_ci if (!ret) 7738c2ecf20Sopenharmony_ci ret = reg_write(client, RJ54N1_HCAPE_WB, wb_right); 7748c2ecf20Sopenharmony_ci if (!ret) 7758c2ecf20Sopenharmony_ci ret = reg_write(client, RJ54N1_VCAPE_WB, wb_bottom); 7768c2ecf20Sopenharmony_ci } 7778c2ecf20Sopenharmony_ci 7788c2ecf20Sopenharmony_ci /* Antiflicker */ 7798c2ecf20Sopenharmony_ci peak = 12 * RJ54N1_MAX_WIDTH * (1 << 14) * resize / rj54n1->tgclk_mhz / 7808c2ecf20Sopenharmony_ci 10000; 7818c2ecf20Sopenharmony_ci peak_50 = peak / 6; 7828c2ecf20Sopenharmony_ci peak_60 = peak / 5; 7838c2ecf20Sopenharmony_ci 7848c2ecf20Sopenharmony_ci if (!ret) 7858c2ecf20Sopenharmony_ci ret = reg_write(client, RJ54N1_PEAK_H, 7868c2ecf20Sopenharmony_ci ((peak_50 >> 4) & 0xf0) | (peak_60 >> 8)); 7878c2ecf20Sopenharmony_ci if (!ret) 7888c2ecf20Sopenharmony_ci ret = reg_write(client, RJ54N1_PEAK_50, peak_50); 7898c2ecf20Sopenharmony_ci if (!ret) 7908c2ecf20Sopenharmony_ci ret = reg_write(client, RJ54N1_PEAK_60, peak_60); 7918c2ecf20Sopenharmony_ci if (!ret) 7928c2ecf20Sopenharmony_ci ret = reg_write(client, RJ54N1_PEAK_DIFF, peak / 150); 7938c2ecf20Sopenharmony_ci 7948c2ecf20Sopenharmony_ci /* Start resizing */ 7958c2ecf20Sopenharmony_ci if (!ret) 7968c2ecf20Sopenharmony_ci ret = reg_write(client, RJ54N1_RESIZE_CONTROL, 7978c2ecf20Sopenharmony_ci RESIZE_HOLD_SEL | RESIZE_GO | 1); 7988c2ecf20Sopenharmony_ci 7998c2ecf20Sopenharmony_ci if (ret < 0) 8008c2ecf20Sopenharmony_ci return ret; 8018c2ecf20Sopenharmony_ci 8028c2ecf20Sopenharmony_ci /* Constant taken from manufacturer's example */ 8038c2ecf20Sopenharmony_ci msleep(230); 8048c2ecf20Sopenharmony_ci 8058c2ecf20Sopenharmony_ci ret = reg_write(client, RJ54N1_RESIZE_CONTROL, RESIZE_HOLD_SEL | 1); 8068c2ecf20Sopenharmony_ci if (ret < 0) 8078c2ecf20Sopenharmony_ci return ret; 8088c2ecf20Sopenharmony_ci 8098c2ecf20Sopenharmony_ci *in_w = (output_w * resize + 512) / 1024; 8108c2ecf20Sopenharmony_ci *in_h = (output_h * resize + 512) / 1024; 8118c2ecf20Sopenharmony_ci *out_w = output_w; 8128c2ecf20Sopenharmony_ci *out_h = output_h; 8138c2ecf20Sopenharmony_ci 8148c2ecf20Sopenharmony_ci dev_dbg(&client->dev, "Scaled for %dx%d : %u = %ux%u, skip %u\n", 8158c2ecf20Sopenharmony_ci *in_w, *in_h, resize, output_w, output_h, skip); 8168c2ecf20Sopenharmony_ci 8178c2ecf20Sopenharmony_ci return resize; 8188c2ecf20Sopenharmony_ci} 8198c2ecf20Sopenharmony_ci 8208c2ecf20Sopenharmony_cistatic int rj54n1_set_clock(struct i2c_client *client) 8218c2ecf20Sopenharmony_ci{ 8228c2ecf20Sopenharmony_ci struct rj54n1 *rj54n1 = to_rj54n1(client); 8238c2ecf20Sopenharmony_ci int ret; 8248c2ecf20Sopenharmony_ci 8258c2ecf20Sopenharmony_ci /* Enable external clock */ 8268c2ecf20Sopenharmony_ci ret = reg_write(client, RJ54N1_RESET_STANDBY, E_EXCLK | SOFT_STDBY); 8278c2ecf20Sopenharmony_ci /* Leave stand-by. Note: use this when implementing suspend / resume */ 8288c2ecf20Sopenharmony_ci if (!ret) 8298c2ecf20Sopenharmony_ci ret = reg_write(client, RJ54N1_RESET_STANDBY, E_EXCLK); 8308c2ecf20Sopenharmony_ci 8318c2ecf20Sopenharmony_ci if (!ret) 8328c2ecf20Sopenharmony_ci ret = reg_write(client, RJ54N1_PLL_L, PLL_L); 8338c2ecf20Sopenharmony_ci if (!ret) 8348c2ecf20Sopenharmony_ci ret = reg_write(client, RJ54N1_PLL_N, PLL_N); 8358c2ecf20Sopenharmony_ci 8368c2ecf20Sopenharmony_ci /* TGCLK dividers */ 8378c2ecf20Sopenharmony_ci if (!ret) 8388c2ecf20Sopenharmony_ci ret = reg_write(client, RJ54N1_RATIO_TG, 8398c2ecf20Sopenharmony_ci rj54n1->clk_div.ratio_tg); 8408c2ecf20Sopenharmony_ci if (!ret) 8418c2ecf20Sopenharmony_ci ret = reg_write(client, RJ54N1_RATIO_T, 8428c2ecf20Sopenharmony_ci rj54n1->clk_div.ratio_t); 8438c2ecf20Sopenharmony_ci if (!ret) 8448c2ecf20Sopenharmony_ci ret = reg_write(client, RJ54N1_RATIO_R, 8458c2ecf20Sopenharmony_ci rj54n1->clk_div.ratio_r); 8468c2ecf20Sopenharmony_ci 8478c2ecf20Sopenharmony_ci /* Enable TGCLK & RAMP */ 8488c2ecf20Sopenharmony_ci if (!ret) 8498c2ecf20Sopenharmony_ci ret = reg_write(client, RJ54N1_RAMP_TGCLK_EN, 3); 8508c2ecf20Sopenharmony_ci 8518c2ecf20Sopenharmony_ci /* Disable clock output */ 8528c2ecf20Sopenharmony_ci if (!ret) 8538c2ecf20Sopenharmony_ci ret = reg_write(client, RJ54N1_OCLK_DSP, 0); 8548c2ecf20Sopenharmony_ci 8558c2ecf20Sopenharmony_ci /* Set divisors */ 8568c2ecf20Sopenharmony_ci if (!ret) 8578c2ecf20Sopenharmony_ci ret = reg_write(client, RJ54N1_RATIO_OP, 8588c2ecf20Sopenharmony_ci rj54n1->clk_div.ratio_op); 8598c2ecf20Sopenharmony_ci if (!ret) 8608c2ecf20Sopenharmony_ci ret = reg_write(client, RJ54N1_RATIO_O, 8618c2ecf20Sopenharmony_ci rj54n1->clk_div.ratio_o); 8628c2ecf20Sopenharmony_ci 8638c2ecf20Sopenharmony_ci /* Enable OCLK */ 8648c2ecf20Sopenharmony_ci if (!ret) 8658c2ecf20Sopenharmony_ci ret = reg_write(client, RJ54N1_OCLK_SEL_EN, 1); 8668c2ecf20Sopenharmony_ci 8678c2ecf20Sopenharmony_ci /* Use PLL for Timing Generator, write 2 to reserved bits */ 8688c2ecf20Sopenharmony_ci if (!ret) 8698c2ecf20Sopenharmony_ci ret = reg_write(client, RJ54N1_TG_BYPASS, 2); 8708c2ecf20Sopenharmony_ci 8718c2ecf20Sopenharmony_ci /* Take sensor out of reset */ 8728c2ecf20Sopenharmony_ci if (!ret) 8738c2ecf20Sopenharmony_ci ret = reg_write(client, RJ54N1_RESET_STANDBY, 8748c2ecf20Sopenharmony_ci E_EXCLK | SEN_RSTX); 8758c2ecf20Sopenharmony_ci /* Enable PLL */ 8768c2ecf20Sopenharmony_ci if (!ret) 8778c2ecf20Sopenharmony_ci ret = reg_write(client, RJ54N1_PLL_EN, 1); 8788c2ecf20Sopenharmony_ci 8798c2ecf20Sopenharmony_ci /* Wait for PLL to stabilise */ 8808c2ecf20Sopenharmony_ci msleep(10); 8818c2ecf20Sopenharmony_ci 8828c2ecf20Sopenharmony_ci /* Enable clock to frequency divider */ 8838c2ecf20Sopenharmony_ci if (!ret) 8848c2ecf20Sopenharmony_ci ret = reg_write(client, RJ54N1_CLK_RST, 1); 8858c2ecf20Sopenharmony_ci 8868c2ecf20Sopenharmony_ci if (!ret) 8878c2ecf20Sopenharmony_ci ret = reg_read(client, RJ54N1_CLK_RST); 8888c2ecf20Sopenharmony_ci if (ret != 1) { 8898c2ecf20Sopenharmony_ci dev_err(&client->dev, 8908c2ecf20Sopenharmony_ci "Resetting RJ54N1CB0C clock failed: %d!\n", ret); 8918c2ecf20Sopenharmony_ci return -EIO; 8928c2ecf20Sopenharmony_ci } 8938c2ecf20Sopenharmony_ci 8948c2ecf20Sopenharmony_ci /* Start the PLL */ 8958c2ecf20Sopenharmony_ci ret = reg_set(client, RJ54N1_OCLK_DSP, 1, 1); 8968c2ecf20Sopenharmony_ci 8978c2ecf20Sopenharmony_ci /* Enable OCLK */ 8988c2ecf20Sopenharmony_ci if (!ret) 8998c2ecf20Sopenharmony_ci ret = reg_write(client, RJ54N1_OCLK_SEL_EN, 1); 9008c2ecf20Sopenharmony_ci 9018c2ecf20Sopenharmony_ci return ret; 9028c2ecf20Sopenharmony_ci} 9038c2ecf20Sopenharmony_ci 9048c2ecf20Sopenharmony_cistatic int rj54n1_reg_init(struct i2c_client *client) 9058c2ecf20Sopenharmony_ci{ 9068c2ecf20Sopenharmony_ci struct rj54n1 *rj54n1 = to_rj54n1(client); 9078c2ecf20Sopenharmony_ci int ret = rj54n1_set_clock(client); 9088c2ecf20Sopenharmony_ci 9098c2ecf20Sopenharmony_ci if (!ret) 9108c2ecf20Sopenharmony_ci ret = reg_write_multiple(client, bank_7, ARRAY_SIZE(bank_7)); 9118c2ecf20Sopenharmony_ci if (!ret) 9128c2ecf20Sopenharmony_ci ret = reg_write_multiple(client, bank_10, ARRAY_SIZE(bank_10)); 9138c2ecf20Sopenharmony_ci 9148c2ecf20Sopenharmony_ci /* Set binning divisors */ 9158c2ecf20Sopenharmony_ci if (!ret) 9168c2ecf20Sopenharmony_ci ret = reg_write(client, RJ54N1_SCALE_1_2_LEV, 3 | (7 << 4)); 9178c2ecf20Sopenharmony_ci if (!ret) 9188c2ecf20Sopenharmony_ci ret = reg_write(client, RJ54N1_SCALE_4_LEV, 0xf); 9198c2ecf20Sopenharmony_ci 9208c2ecf20Sopenharmony_ci /* Switch to fixed resize mode */ 9218c2ecf20Sopenharmony_ci if (!ret) 9228c2ecf20Sopenharmony_ci ret = reg_write(client, RJ54N1_RESIZE_CONTROL, 9238c2ecf20Sopenharmony_ci RESIZE_HOLD_SEL | 1); 9248c2ecf20Sopenharmony_ci 9258c2ecf20Sopenharmony_ci /* Set gain */ 9268c2ecf20Sopenharmony_ci if (!ret) 9278c2ecf20Sopenharmony_ci ret = reg_write(client, RJ54N1_Y_GAIN, 0x84); 9288c2ecf20Sopenharmony_ci 9298c2ecf20Sopenharmony_ci /* 9308c2ecf20Sopenharmony_ci * Mirror the image back: default is upside down and left-to-right... 9318c2ecf20Sopenharmony_ci * Set manual preview / still shot switching 9328c2ecf20Sopenharmony_ci */ 9338c2ecf20Sopenharmony_ci if (!ret) 9348c2ecf20Sopenharmony_ci ret = reg_write(client, RJ54N1_MIRROR_STILL_MODE, 0x27); 9358c2ecf20Sopenharmony_ci 9368c2ecf20Sopenharmony_ci if (!ret) 9378c2ecf20Sopenharmony_ci ret = reg_write_multiple(client, bank_4, ARRAY_SIZE(bank_4)); 9388c2ecf20Sopenharmony_ci 9398c2ecf20Sopenharmony_ci /* Auto exposure area */ 9408c2ecf20Sopenharmony_ci if (!ret) 9418c2ecf20Sopenharmony_ci ret = reg_write(client, RJ54N1_EXPOSURE_CONTROL, 0x80); 9428c2ecf20Sopenharmony_ci /* Check current auto WB config */ 9438c2ecf20Sopenharmony_ci if (!ret) 9448c2ecf20Sopenharmony_ci ret = reg_read(client, RJ54N1_WB_SEL_WEIGHT_I); 9458c2ecf20Sopenharmony_ci if (ret >= 0) { 9468c2ecf20Sopenharmony_ci rj54n1->auto_wb = ret & 0x80; 9478c2ecf20Sopenharmony_ci ret = reg_write_multiple(client, bank_5, ARRAY_SIZE(bank_5)); 9488c2ecf20Sopenharmony_ci } 9498c2ecf20Sopenharmony_ci if (!ret) 9508c2ecf20Sopenharmony_ci ret = reg_write_multiple(client, bank_8, ARRAY_SIZE(bank_8)); 9518c2ecf20Sopenharmony_ci 9528c2ecf20Sopenharmony_ci if (!ret) 9538c2ecf20Sopenharmony_ci ret = reg_write(client, RJ54N1_RESET_STANDBY, 9548c2ecf20Sopenharmony_ci E_EXCLK | DSP_RSTX | SEN_RSTX); 9558c2ecf20Sopenharmony_ci 9568c2ecf20Sopenharmony_ci /* Commit init */ 9578c2ecf20Sopenharmony_ci if (!ret) 9588c2ecf20Sopenharmony_ci ret = rj54n1_commit(client); 9598c2ecf20Sopenharmony_ci 9608c2ecf20Sopenharmony_ci /* Take DSP, TG, sensor out of reset */ 9618c2ecf20Sopenharmony_ci if (!ret) 9628c2ecf20Sopenharmony_ci ret = reg_write(client, RJ54N1_RESET_STANDBY, 9638c2ecf20Sopenharmony_ci E_EXCLK | DSP_RSTX | TG_RSTX | SEN_RSTX); 9648c2ecf20Sopenharmony_ci 9658c2ecf20Sopenharmony_ci /* Start register update? Same register as 0x?FE in many bank_* sets */ 9668c2ecf20Sopenharmony_ci if (!ret) 9678c2ecf20Sopenharmony_ci ret = reg_write(client, RJ54N1_FWFLG, 2); 9688c2ecf20Sopenharmony_ci 9698c2ecf20Sopenharmony_ci /* Constant taken from manufacturer's example */ 9708c2ecf20Sopenharmony_ci msleep(700); 9718c2ecf20Sopenharmony_ci 9728c2ecf20Sopenharmony_ci return ret; 9738c2ecf20Sopenharmony_ci} 9748c2ecf20Sopenharmony_ci 9758c2ecf20Sopenharmony_cistatic int rj54n1_set_fmt(struct v4l2_subdev *sd, 9768c2ecf20Sopenharmony_ci struct v4l2_subdev_pad_config *cfg, 9778c2ecf20Sopenharmony_ci struct v4l2_subdev_format *format) 9788c2ecf20Sopenharmony_ci{ 9798c2ecf20Sopenharmony_ci struct v4l2_mbus_framefmt *mf = &format->format; 9808c2ecf20Sopenharmony_ci struct i2c_client *client = v4l2_get_subdevdata(sd); 9818c2ecf20Sopenharmony_ci struct rj54n1 *rj54n1 = to_rj54n1(client); 9828c2ecf20Sopenharmony_ci const struct rj54n1_datafmt *fmt; 9838c2ecf20Sopenharmony_ci int output_w, output_h, max_w, max_h, 9848c2ecf20Sopenharmony_ci input_w = rj54n1->rect.width, input_h = rj54n1->rect.height; 9858c2ecf20Sopenharmony_ci int align = mf->code == MEDIA_BUS_FMT_SBGGR10_1X10 || 9868c2ecf20Sopenharmony_ci mf->code == MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_BE || 9878c2ecf20Sopenharmony_ci mf->code == MEDIA_BUS_FMT_SBGGR10_2X8_PADLO_BE || 9888c2ecf20Sopenharmony_ci mf->code == MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE || 9898c2ecf20Sopenharmony_ci mf->code == MEDIA_BUS_FMT_SBGGR10_2X8_PADLO_LE; 9908c2ecf20Sopenharmony_ci int ret; 9918c2ecf20Sopenharmony_ci 9928c2ecf20Sopenharmony_ci if (format->pad) 9938c2ecf20Sopenharmony_ci return -EINVAL; 9948c2ecf20Sopenharmony_ci 9958c2ecf20Sopenharmony_ci dev_dbg(&client->dev, "%s: code = %d, width = %u, height = %u\n", 9968c2ecf20Sopenharmony_ci __func__, mf->code, mf->width, mf->height); 9978c2ecf20Sopenharmony_ci 9988c2ecf20Sopenharmony_ci fmt = rj54n1_find_datafmt(mf->code, rj54n1_colour_fmts, 9998c2ecf20Sopenharmony_ci ARRAY_SIZE(rj54n1_colour_fmts)); 10008c2ecf20Sopenharmony_ci if (!fmt) { 10018c2ecf20Sopenharmony_ci fmt = rj54n1->fmt; 10028c2ecf20Sopenharmony_ci mf->code = fmt->code; 10038c2ecf20Sopenharmony_ci } 10048c2ecf20Sopenharmony_ci 10058c2ecf20Sopenharmony_ci mf->field = V4L2_FIELD_NONE; 10068c2ecf20Sopenharmony_ci mf->colorspace = fmt->colorspace; 10078c2ecf20Sopenharmony_ci 10088c2ecf20Sopenharmony_ci v4l_bound_align_image(&mf->width, 112, RJ54N1_MAX_WIDTH, align, 10098c2ecf20Sopenharmony_ci &mf->height, 84, RJ54N1_MAX_HEIGHT, align, 0); 10108c2ecf20Sopenharmony_ci 10118c2ecf20Sopenharmony_ci if (format->which == V4L2_SUBDEV_FORMAT_TRY) { 10128c2ecf20Sopenharmony_ci cfg->try_fmt = *mf; 10138c2ecf20Sopenharmony_ci return 0; 10148c2ecf20Sopenharmony_ci } 10158c2ecf20Sopenharmony_ci 10168c2ecf20Sopenharmony_ci /* 10178c2ecf20Sopenharmony_ci * Verify if the sensor has just been powered on. TODO: replace this 10188c2ecf20Sopenharmony_ci * with proper PM, when a suitable API is available. 10198c2ecf20Sopenharmony_ci */ 10208c2ecf20Sopenharmony_ci ret = reg_read(client, RJ54N1_RESET_STANDBY); 10218c2ecf20Sopenharmony_ci if (ret < 0) 10228c2ecf20Sopenharmony_ci return ret; 10238c2ecf20Sopenharmony_ci 10248c2ecf20Sopenharmony_ci if (!(ret & E_EXCLK)) { 10258c2ecf20Sopenharmony_ci ret = rj54n1_reg_init(client); 10268c2ecf20Sopenharmony_ci if (ret < 0) 10278c2ecf20Sopenharmony_ci return ret; 10288c2ecf20Sopenharmony_ci } 10298c2ecf20Sopenharmony_ci 10308c2ecf20Sopenharmony_ci /* RA_SEL_UL is only relevant for raw modes, ignored otherwise. */ 10318c2ecf20Sopenharmony_ci switch (mf->code) { 10328c2ecf20Sopenharmony_ci case MEDIA_BUS_FMT_YUYV8_2X8: 10338c2ecf20Sopenharmony_ci ret = reg_write(client, RJ54N1_OUT_SEL, 0); 10348c2ecf20Sopenharmony_ci if (!ret) 10358c2ecf20Sopenharmony_ci ret = reg_set(client, RJ54N1_BYTE_SWAP, 8, 8); 10368c2ecf20Sopenharmony_ci break; 10378c2ecf20Sopenharmony_ci case MEDIA_BUS_FMT_YVYU8_2X8: 10388c2ecf20Sopenharmony_ci ret = reg_write(client, RJ54N1_OUT_SEL, 0); 10398c2ecf20Sopenharmony_ci if (!ret) 10408c2ecf20Sopenharmony_ci ret = reg_set(client, RJ54N1_BYTE_SWAP, 0, 8); 10418c2ecf20Sopenharmony_ci break; 10428c2ecf20Sopenharmony_ci case MEDIA_BUS_FMT_RGB565_2X8_LE: 10438c2ecf20Sopenharmony_ci ret = reg_write(client, RJ54N1_OUT_SEL, 0x11); 10448c2ecf20Sopenharmony_ci if (!ret) 10458c2ecf20Sopenharmony_ci ret = reg_set(client, RJ54N1_BYTE_SWAP, 8, 8); 10468c2ecf20Sopenharmony_ci break; 10478c2ecf20Sopenharmony_ci case MEDIA_BUS_FMT_RGB565_2X8_BE: 10488c2ecf20Sopenharmony_ci ret = reg_write(client, RJ54N1_OUT_SEL, 0x11); 10498c2ecf20Sopenharmony_ci if (!ret) 10508c2ecf20Sopenharmony_ci ret = reg_set(client, RJ54N1_BYTE_SWAP, 0, 8); 10518c2ecf20Sopenharmony_ci break; 10528c2ecf20Sopenharmony_ci case MEDIA_BUS_FMT_SBGGR10_2X8_PADLO_LE: 10538c2ecf20Sopenharmony_ci ret = reg_write(client, RJ54N1_OUT_SEL, 4); 10548c2ecf20Sopenharmony_ci if (!ret) 10558c2ecf20Sopenharmony_ci ret = reg_set(client, RJ54N1_BYTE_SWAP, 8, 8); 10568c2ecf20Sopenharmony_ci if (!ret) 10578c2ecf20Sopenharmony_ci ret = reg_write(client, RJ54N1_RA_SEL_UL, 0); 10588c2ecf20Sopenharmony_ci break; 10598c2ecf20Sopenharmony_ci case MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE: 10608c2ecf20Sopenharmony_ci ret = reg_write(client, RJ54N1_OUT_SEL, 4); 10618c2ecf20Sopenharmony_ci if (!ret) 10628c2ecf20Sopenharmony_ci ret = reg_set(client, RJ54N1_BYTE_SWAP, 8, 8); 10638c2ecf20Sopenharmony_ci if (!ret) 10648c2ecf20Sopenharmony_ci ret = reg_write(client, RJ54N1_RA_SEL_UL, 8); 10658c2ecf20Sopenharmony_ci break; 10668c2ecf20Sopenharmony_ci case MEDIA_BUS_FMT_SBGGR10_2X8_PADLO_BE: 10678c2ecf20Sopenharmony_ci ret = reg_write(client, RJ54N1_OUT_SEL, 4); 10688c2ecf20Sopenharmony_ci if (!ret) 10698c2ecf20Sopenharmony_ci ret = reg_set(client, RJ54N1_BYTE_SWAP, 0, 8); 10708c2ecf20Sopenharmony_ci if (!ret) 10718c2ecf20Sopenharmony_ci ret = reg_write(client, RJ54N1_RA_SEL_UL, 0); 10728c2ecf20Sopenharmony_ci break; 10738c2ecf20Sopenharmony_ci case MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_BE: 10748c2ecf20Sopenharmony_ci ret = reg_write(client, RJ54N1_OUT_SEL, 4); 10758c2ecf20Sopenharmony_ci if (!ret) 10768c2ecf20Sopenharmony_ci ret = reg_set(client, RJ54N1_BYTE_SWAP, 0, 8); 10778c2ecf20Sopenharmony_ci if (!ret) 10788c2ecf20Sopenharmony_ci ret = reg_write(client, RJ54N1_RA_SEL_UL, 8); 10798c2ecf20Sopenharmony_ci break; 10808c2ecf20Sopenharmony_ci case MEDIA_BUS_FMT_SBGGR10_1X10: 10818c2ecf20Sopenharmony_ci ret = reg_write(client, RJ54N1_OUT_SEL, 5); 10828c2ecf20Sopenharmony_ci break; 10838c2ecf20Sopenharmony_ci default: 10848c2ecf20Sopenharmony_ci ret = -EINVAL; 10858c2ecf20Sopenharmony_ci } 10868c2ecf20Sopenharmony_ci 10878c2ecf20Sopenharmony_ci /* Special case: a raw mode with 10 bits of data per clock tick */ 10888c2ecf20Sopenharmony_ci if (!ret) 10898c2ecf20Sopenharmony_ci ret = reg_set(client, RJ54N1_OCLK_SEL_EN, 10908c2ecf20Sopenharmony_ci (mf->code == MEDIA_BUS_FMT_SBGGR10_1X10) << 1, 2); 10918c2ecf20Sopenharmony_ci 10928c2ecf20Sopenharmony_ci if (ret < 0) 10938c2ecf20Sopenharmony_ci return ret; 10948c2ecf20Sopenharmony_ci 10958c2ecf20Sopenharmony_ci /* Supported scales 1:1 >= scale > 1:16 */ 10968c2ecf20Sopenharmony_ci max_w = mf->width * (16 * 1024 - 1) / 1024; 10978c2ecf20Sopenharmony_ci if (input_w > max_w) 10988c2ecf20Sopenharmony_ci input_w = max_w; 10998c2ecf20Sopenharmony_ci max_h = mf->height * (16 * 1024 - 1) / 1024; 11008c2ecf20Sopenharmony_ci if (input_h > max_h) 11018c2ecf20Sopenharmony_ci input_h = max_h; 11028c2ecf20Sopenharmony_ci 11038c2ecf20Sopenharmony_ci output_w = mf->width; 11048c2ecf20Sopenharmony_ci output_h = mf->height; 11058c2ecf20Sopenharmony_ci 11068c2ecf20Sopenharmony_ci ret = rj54n1_sensor_scale(sd, &input_w, &input_h, &output_w, &output_h); 11078c2ecf20Sopenharmony_ci if (ret < 0) 11088c2ecf20Sopenharmony_ci return ret; 11098c2ecf20Sopenharmony_ci 11108c2ecf20Sopenharmony_ci fmt = rj54n1_find_datafmt(mf->code, rj54n1_colour_fmts, 11118c2ecf20Sopenharmony_ci ARRAY_SIZE(rj54n1_colour_fmts)); 11128c2ecf20Sopenharmony_ci 11138c2ecf20Sopenharmony_ci rj54n1->fmt = fmt; 11148c2ecf20Sopenharmony_ci rj54n1->resize = ret; 11158c2ecf20Sopenharmony_ci rj54n1->rect.width = input_w; 11168c2ecf20Sopenharmony_ci rj54n1->rect.height = input_h; 11178c2ecf20Sopenharmony_ci rj54n1->width = output_w; 11188c2ecf20Sopenharmony_ci rj54n1->height = output_h; 11198c2ecf20Sopenharmony_ci 11208c2ecf20Sopenharmony_ci mf->width = output_w; 11218c2ecf20Sopenharmony_ci mf->height = output_h; 11228c2ecf20Sopenharmony_ci mf->field = V4L2_FIELD_NONE; 11238c2ecf20Sopenharmony_ci mf->colorspace = fmt->colorspace; 11248c2ecf20Sopenharmony_ci 11258c2ecf20Sopenharmony_ci return 0; 11268c2ecf20Sopenharmony_ci} 11278c2ecf20Sopenharmony_ci 11288c2ecf20Sopenharmony_ci#ifdef CONFIG_VIDEO_ADV_DEBUG 11298c2ecf20Sopenharmony_cistatic int rj54n1_g_register(struct v4l2_subdev *sd, 11308c2ecf20Sopenharmony_ci struct v4l2_dbg_register *reg) 11318c2ecf20Sopenharmony_ci{ 11328c2ecf20Sopenharmony_ci struct i2c_client *client = v4l2_get_subdevdata(sd); 11338c2ecf20Sopenharmony_ci 11348c2ecf20Sopenharmony_ci if (reg->reg < 0x400 || reg->reg > 0x1fff) 11358c2ecf20Sopenharmony_ci /* Registers > 0x0800 are only available from Sharp support */ 11368c2ecf20Sopenharmony_ci return -EINVAL; 11378c2ecf20Sopenharmony_ci 11388c2ecf20Sopenharmony_ci reg->size = 1; 11398c2ecf20Sopenharmony_ci reg->val = reg_read(client, reg->reg); 11408c2ecf20Sopenharmony_ci 11418c2ecf20Sopenharmony_ci if (reg->val > 0xff) 11428c2ecf20Sopenharmony_ci return -EIO; 11438c2ecf20Sopenharmony_ci 11448c2ecf20Sopenharmony_ci return 0; 11458c2ecf20Sopenharmony_ci} 11468c2ecf20Sopenharmony_ci 11478c2ecf20Sopenharmony_cistatic int rj54n1_s_register(struct v4l2_subdev *sd, 11488c2ecf20Sopenharmony_ci const struct v4l2_dbg_register *reg) 11498c2ecf20Sopenharmony_ci{ 11508c2ecf20Sopenharmony_ci struct i2c_client *client = v4l2_get_subdevdata(sd); 11518c2ecf20Sopenharmony_ci 11528c2ecf20Sopenharmony_ci if (reg->reg < 0x400 || reg->reg > 0x1fff) 11538c2ecf20Sopenharmony_ci /* Registers >= 0x0800 are only available from Sharp support */ 11548c2ecf20Sopenharmony_ci return -EINVAL; 11558c2ecf20Sopenharmony_ci 11568c2ecf20Sopenharmony_ci if (reg_write(client, reg->reg, reg->val) < 0) 11578c2ecf20Sopenharmony_ci return -EIO; 11588c2ecf20Sopenharmony_ci 11598c2ecf20Sopenharmony_ci return 0; 11608c2ecf20Sopenharmony_ci} 11618c2ecf20Sopenharmony_ci#endif 11628c2ecf20Sopenharmony_ci 11638c2ecf20Sopenharmony_cistatic int rj54n1_s_power(struct v4l2_subdev *sd, int on) 11648c2ecf20Sopenharmony_ci{ 11658c2ecf20Sopenharmony_ci struct i2c_client *client = v4l2_get_subdevdata(sd); 11668c2ecf20Sopenharmony_ci struct rj54n1 *rj54n1 = to_rj54n1(client); 11678c2ecf20Sopenharmony_ci 11688c2ecf20Sopenharmony_ci if (on) { 11698c2ecf20Sopenharmony_ci if (rj54n1->pwup_gpio) 11708c2ecf20Sopenharmony_ci gpiod_set_value(rj54n1->pwup_gpio, 1); 11718c2ecf20Sopenharmony_ci if (rj54n1->enable_gpio) 11728c2ecf20Sopenharmony_ci gpiod_set_value(rj54n1->enable_gpio, 1); 11738c2ecf20Sopenharmony_ci 11748c2ecf20Sopenharmony_ci msleep(1); 11758c2ecf20Sopenharmony_ci 11768c2ecf20Sopenharmony_ci return clk_prepare_enable(rj54n1->clk); 11778c2ecf20Sopenharmony_ci } 11788c2ecf20Sopenharmony_ci 11798c2ecf20Sopenharmony_ci clk_disable_unprepare(rj54n1->clk); 11808c2ecf20Sopenharmony_ci 11818c2ecf20Sopenharmony_ci if (rj54n1->enable_gpio) 11828c2ecf20Sopenharmony_ci gpiod_set_value(rj54n1->enable_gpio, 0); 11838c2ecf20Sopenharmony_ci if (rj54n1->pwup_gpio) 11848c2ecf20Sopenharmony_ci gpiod_set_value(rj54n1->pwup_gpio, 0); 11858c2ecf20Sopenharmony_ci 11868c2ecf20Sopenharmony_ci return 0; 11878c2ecf20Sopenharmony_ci} 11888c2ecf20Sopenharmony_ci 11898c2ecf20Sopenharmony_cistatic int rj54n1_s_ctrl(struct v4l2_ctrl *ctrl) 11908c2ecf20Sopenharmony_ci{ 11918c2ecf20Sopenharmony_ci struct rj54n1 *rj54n1 = container_of(ctrl->handler, struct rj54n1, hdl); 11928c2ecf20Sopenharmony_ci struct v4l2_subdev *sd = &rj54n1->subdev; 11938c2ecf20Sopenharmony_ci struct i2c_client *client = v4l2_get_subdevdata(sd); 11948c2ecf20Sopenharmony_ci int data; 11958c2ecf20Sopenharmony_ci 11968c2ecf20Sopenharmony_ci switch (ctrl->id) { 11978c2ecf20Sopenharmony_ci case V4L2_CID_VFLIP: 11988c2ecf20Sopenharmony_ci if (ctrl->val) 11998c2ecf20Sopenharmony_ci data = reg_set(client, RJ54N1_MIRROR_STILL_MODE, 0, 1); 12008c2ecf20Sopenharmony_ci else 12018c2ecf20Sopenharmony_ci data = reg_set(client, RJ54N1_MIRROR_STILL_MODE, 1, 1); 12028c2ecf20Sopenharmony_ci if (data < 0) 12038c2ecf20Sopenharmony_ci return -EIO; 12048c2ecf20Sopenharmony_ci return 0; 12058c2ecf20Sopenharmony_ci case V4L2_CID_HFLIP: 12068c2ecf20Sopenharmony_ci if (ctrl->val) 12078c2ecf20Sopenharmony_ci data = reg_set(client, RJ54N1_MIRROR_STILL_MODE, 0, 2); 12088c2ecf20Sopenharmony_ci else 12098c2ecf20Sopenharmony_ci data = reg_set(client, RJ54N1_MIRROR_STILL_MODE, 2, 2); 12108c2ecf20Sopenharmony_ci if (data < 0) 12118c2ecf20Sopenharmony_ci return -EIO; 12128c2ecf20Sopenharmony_ci return 0; 12138c2ecf20Sopenharmony_ci case V4L2_CID_GAIN: 12148c2ecf20Sopenharmony_ci if (reg_write(client, RJ54N1_Y_GAIN, ctrl->val * 2) < 0) 12158c2ecf20Sopenharmony_ci return -EIO; 12168c2ecf20Sopenharmony_ci return 0; 12178c2ecf20Sopenharmony_ci case V4L2_CID_AUTO_WHITE_BALANCE: 12188c2ecf20Sopenharmony_ci /* Auto WB area - whole image */ 12198c2ecf20Sopenharmony_ci if (reg_set(client, RJ54N1_WB_SEL_WEIGHT_I, ctrl->val << 7, 12208c2ecf20Sopenharmony_ci 0x80) < 0) 12218c2ecf20Sopenharmony_ci return -EIO; 12228c2ecf20Sopenharmony_ci rj54n1->auto_wb = ctrl->val; 12238c2ecf20Sopenharmony_ci return 0; 12248c2ecf20Sopenharmony_ci } 12258c2ecf20Sopenharmony_ci 12268c2ecf20Sopenharmony_ci return -EINVAL; 12278c2ecf20Sopenharmony_ci} 12288c2ecf20Sopenharmony_ci 12298c2ecf20Sopenharmony_cistatic const struct v4l2_ctrl_ops rj54n1_ctrl_ops = { 12308c2ecf20Sopenharmony_ci .s_ctrl = rj54n1_s_ctrl, 12318c2ecf20Sopenharmony_ci}; 12328c2ecf20Sopenharmony_ci 12338c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_core_ops rj54n1_subdev_core_ops = { 12348c2ecf20Sopenharmony_ci#ifdef CONFIG_VIDEO_ADV_DEBUG 12358c2ecf20Sopenharmony_ci .g_register = rj54n1_g_register, 12368c2ecf20Sopenharmony_ci .s_register = rj54n1_s_register, 12378c2ecf20Sopenharmony_ci#endif 12388c2ecf20Sopenharmony_ci .s_power = rj54n1_s_power, 12398c2ecf20Sopenharmony_ci}; 12408c2ecf20Sopenharmony_ci 12418c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_video_ops rj54n1_subdev_video_ops = { 12428c2ecf20Sopenharmony_ci .s_stream = rj54n1_s_stream, 12438c2ecf20Sopenharmony_ci}; 12448c2ecf20Sopenharmony_ci 12458c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_pad_ops rj54n1_subdev_pad_ops = { 12468c2ecf20Sopenharmony_ci .enum_mbus_code = rj54n1_enum_mbus_code, 12478c2ecf20Sopenharmony_ci .get_selection = rj54n1_get_selection, 12488c2ecf20Sopenharmony_ci .set_selection = rj54n1_set_selection, 12498c2ecf20Sopenharmony_ci .get_fmt = rj54n1_get_fmt, 12508c2ecf20Sopenharmony_ci .set_fmt = rj54n1_set_fmt, 12518c2ecf20Sopenharmony_ci}; 12528c2ecf20Sopenharmony_ci 12538c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_ops rj54n1_subdev_ops = { 12548c2ecf20Sopenharmony_ci .core = &rj54n1_subdev_core_ops, 12558c2ecf20Sopenharmony_ci .video = &rj54n1_subdev_video_ops, 12568c2ecf20Sopenharmony_ci .pad = &rj54n1_subdev_pad_ops, 12578c2ecf20Sopenharmony_ci}; 12588c2ecf20Sopenharmony_ci 12598c2ecf20Sopenharmony_ci/* 12608c2ecf20Sopenharmony_ci * Interface active, can use i2c. If it fails, it can indeed mean, that 12618c2ecf20Sopenharmony_ci * this wasn't our capture interface, so, we wait for the right one 12628c2ecf20Sopenharmony_ci */ 12638c2ecf20Sopenharmony_cistatic int rj54n1_video_probe(struct i2c_client *client, 12648c2ecf20Sopenharmony_ci struct rj54n1_pdata *priv) 12658c2ecf20Sopenharmony_ci{ 12668c2ecf20Sopenharmony_ci struct rj54n1 *rj54n1 = to_rj54n1(client); 12678c2ecf20Sopenharmony_ci int data1, data2; 12688c2ecf20Sopenharmony_ci int ret; 12698c2ecf20Sopenharmony_ci 12708c2ecf20Sopenharmony_ci ret = rj54n1_s_power(&rj54n1->subdev, 1); 12718c2ecf20Sopenharmony_ci if (ret < 0) 12728c2ecf20Sopenharmony_ci return ret; 12738c2ecf20Sopenharmony_ci 12748c2ecf20Sopenharmony_ci /* Read out the chip version register */ 12758c2ecf20Sopenharmony_ci data1 = reg_read(client, RJ54N1_DEV_CODE); 12768c2ecf20Sopenharmony_ci data2 = reg_read(client, RJ54N1_DEV_CODE2); 12778c2ecf20Sopenharmony_ci 12788c2ecf20Sopenharmony_ci if (data1 != 0x51 || data2 != 0x10) { 12798c2ecf20Sopenharmony_ci ret = -ENODEV; 12808c2ecf20Sopenharmony_ci dev_info(&client->dev, "No RJ54N1CB0C found, read 0x%x:0x%x\n", 12818c2ecf20Sopenharmony_ci data1, data2); 12828c2ecf20Sopenharmony_ci goto done; 12838c2ecf20Sopenharmony_ci } 12848c2ecf20Sopenharmony_ci 12858c2ecf20Sopenharmony_ci /* Configure IOCTL polarity from the platform data: 0 or 1 << 7. */ 12868c2ecf20Sopenharmony_ci ret = reg_write(client, RJ54N1_IOC, priv->ioctl_high << 7); 12878c2ecf20Sopenharmony_ci if (ret < 0) 12888c2ecf20Sopenharmony_ci goto done; 12898c2ecf20Sopenharmony_ci 12908c2ecf20Sopenharmony_ci dev_info(&client->dev, "Detected a RJ54N1CB0C chip ID 0x%x:0x%x\n", 12918c2ecf20Sopenharmony_ci data1, data2); 12928c2ecf20Sopenharmony_ci 12938c2ecf20Sopenharmony_ci ret = v4l2_ctrl_handler_setup(&rj54n1->hdl); 12948c2ecf20Sopenharmony_ci 12958c2ecf20Sopenharmony_cidone: 12968c2ecf20Sopenharmony_ci rj54n1_s_power(&rj54n1->subdev, 0); 12978c2ecf20Sopenharmony_ci return ret; 12988c2ecf20Sopenharmony_ci} 12998c2ecf20Sopenharmony_ci 13008c2ecf20Sopenharmony_cistatic int rj54n1_probe(struct i2c_client *client, 13018c2ecf20Sopenharmony_ci const struct i2c_device_id *did) 13028c2ecf20Sopenharmony_ci{ 13038c2ecf20Sopenharmony_ci struct rj54n1 *rj54n1; 13048c2ecf20Sopenharmony_ci struct i2c_adapter *adapter = client->adapter; 13058c2ecf20Sopenharmony_ci struct rj54n1_pdata *rj54n1_priv; 13068c2ecf20Sopenharmony_ci int ret; 13078c2ecf20Sopenharmony_ci 13088c2ecf20Sopenharmony_ci if (!client->dev.platform_data) { 13098c2ecf20Sopenharmony_ci dev_err(&client->dev, "RJ54N1CB0C: missing platform data!\n"); 13108c2ecf20Sopenharmony_ci return -EINVAL; 13118c2ecf20Sopenharmony_ci } 13128c2ecf20Sopenharmony_ci 13138c2ecf20Sopenharmony_ci rj54n1_priv = client->dev.platform_data; 13148c2ecf20Sopenharmony_ci 13158c2ecf20Sopenharmony_ci if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { 13168c2ecf20Sopenharmony_ci dev_warn(&adapter->dev, 13178c2ecf20Sopenharmony_ci "I2C-Adapter doesn't support I2C_FUNC_SMBUS_BYTE\n"); 13188c2ecf20Sopenharmony_ci return -EIO; 13198c2ecf20Sopenharmony_ci } 13208c2ecf20Sopenharmony_ci 13218c2ecf20Sopenharmony_ci rj54n1 = devm_kzalloc(&client->dev, sizeof(struct rj54n1), GFP_KERNEL); 13228c2ecf20Sopenharmony_ci if (!rj54n1) 13238c2ecf20Sopenharmony_ci return -ENOMEM; 13248c2ecf20Sopenharmony_ci 13258c2ecf20Sopenharmony_ci v4l2_i2c_subdev_init(&rj54n1->subdev, client, &rj54n1_subdev_ops); 13268c2ecf20Sopenharmony_ci v4l2_ctrl_handler_init(&rj54n1->hdl, 4); 13278c2ecf20Sopenharmony_ci v4l2_ctrl_new_std(&rj54n1->hdl, &rj54n1_ctrl_ops, 13288c2ecf20Sopenharmony_ci V4L2_CID_VFLIP, 0, 1, 1, 0); 13298c2ecf20Sopenharmony_ci v4l2_ctrl_new_std(&rj54n1->hdl, &rj54n1_ctrl_ops, 13308c2ecf20Sopenharmony_ci V4L2_CID_HFLIP, 0, 1, 1, 0); 13318c2ecf20Sopenharmony_ci v4l2_ctrl_new_std(&rj54n1->hdl, &rj54n1_ctrl_ops, 13328c2ecf20Sopenharmony_ci V4L2_CID_GAIN, 0, 127, 1, 66); 13338c2ecf20Sopenharmony_ci v4l2_ctrl_new_std(&rj54n1->hdl, &rj54n1_ctrl_ops, 13348c2ecf20Sopenharmony_ci V4L2_CID_AUTO_WHITE_BALANCE, 0, 1, 1, 1); 13358c2ecf20Sopenharmony_ci rj54n1->subdev.ctrl_handler = &rj54n1->hdl; 13368c2ecf20Sopenharmony_ci if (rj54n1->hdl.error) 13378c2ecf20Sopenharmony_ci return rj54n1->hdl.error; 13388c2ecf20Sopenharmony_ci 13398c2ecf20Sopenharmony_ci rj54n1->clk_div = clk_div; 13408c2ecf20Sopenharmony_ci rj54n1->rect.left = RJ54N1_COLUMN_SKIP; 13418c2ecf20Sopenharmony_ci rj54n1->rect.top = RJ54N1_ROW_SKIP; 13428c2ecf20Sopenharmony_ci rj54n1->rect.width = RJ54N1_MAX_WIDTH; 13438c2ecf20Sopenharmony_ci rj54n1->rect.height = RJ54N1_MAX_HEIGHT; 13448c2ecf20Sopenharmony_ci rj54n1->width = RJ54N1_MAX_WIDTH; 13458c2ecf20Sopenharmony_ci rj54n1->height = RJ54N1_MAX_HEIGHT; 13468c2ecf20Sopenharmony_ci rj54n1->fmt = &rj54n1_colour_fmts[0]; 13478c2ecf20Sopenharmony_ci rj54n1->resize = 1024; 13488c2ecf20Sopenharmony_ci rj54n1->tgclk_mhz = (rj54n1_priv->mclk_freq / PLL_L * PLL_N) / 13498c2ecf20Sopenharmony_ci (clk_div.ratio_tg + 1) / (clk_div.ratio_t + 1); 13508c2ecf20Sopenharmony_ci 13518c2ecf20Sopenharmony_ci rj54n1->clk = clk_get(&client->dev, NULL); 13528c2ecf20Sopenharmony_ci if (IS_ERR(rj54n1->clk)) { 13538c2ecf20Sopenharmony_ci ret = PTR_ERR(rj54n1->clk); 13548c2ecf20Sopenharmony_ci goto err_free_ctrl; 13558c2ecf20Sopenharmony_ci } 13568c2ecf20Sopenharmony_ci 13578c2ecf20Sopenharmony_ci rj54n1->pwup_gpio = gpiod_get_optional(&client->dev, "powerup", 13588c2ecf20Sopenharmony_ci GPIOD_OUT_LOW); 13598c2ecf20Sopenharmony_ci if (IS_ERR(rj54n1->pwup_gpio)) { 13608c2ecf20Sopenharmony_ci dev_info(&client->dev, "Unable to get GPIO \"powerup\": %ld\n", 13618c2ecf20Sopenharmony_ci PTR_ERR(rj54n1->pwup_gpio)); 13628c2ecf20Sopenharmony_ci ret = PTR_ERR(rj54n1->pwup_gpio); 13638c2ecf20Sopenharmony_ci goto err_clk_put; 13648c2ecf20Sopenharmony_ci } 13658c2ecf20Sopenharmony_ci 13668c2ecf20Sopenharmony_ci rj54n1->enable_gpio = gpiod_get_optional(&client->dev, "enable", 13678c2ecf20Sopenharmony_ci GPIOD_OUT_LOW); 13688c2ecf20Sopenharmony_ci if (IS_ERR(rj54n1->enable_gpio)) { 13698c2ecf20Sopenharmony_ci dev_info(&client->dev, "Unable to get GPIO \"enable\": %ld\n", 13708c2ecf20Sopenharmony_ci PTR_ERR(rj54n1->enable_gpio)); 13718c2ecf20Sopenharmony_ci ret = PTR_ERR(rj54n1->enable_gpio); 13728c2ecf20Sopenharmony_ci goto err_gpio_put; 13738c2ecf20Sopenharmony_ci } 13748c2ecf20Sopenharmony_ci 13758c2ecf20Sopenharmony_ci ret = rj54n1_video_probe(client, rj54n1_priv); 13768c2ecf20Sopenharmony_ci if (ret < 0) 13778c2ecf20Sopenharmony_ci goto err_gpio_put; 13788c2ecf20Sopenharmony_ci 13798c2ecf20Sopenharmony_ci ret = v4l2_async_register_subdev(&rj54n1->subdev); 13808c2ecf20Sopenharmony_ci if (ret) 13818c2ecf20Sopenharmony_ci goto err_gpio_put; 13828c2ecf20Sopenharmony_ci 13838c2ecf20Sopenharmony_ci return 0; 13848c2ecf20Sopenharmony_ci 13858c2ecf20Sopenharmony_cierr_gpio_put: 13868c2ecf20Sopenharmony_ci if (rj54n1->enable_gpio) 13878c2ecf20Sopenharmony_ci gpiod_put(rj54n1->enable_gpio); 13888c2ecf20Sopenharmony_ci 13898c2ecf20Sopenharmony_ci if (rj54n1->pwup_gpio) 13908c2ecf20Sopenharmony_ci gpiod_put(rj54n1->pwup_gpio); 13918c2ecf20Sopenharmony_ci 13928c2ecf20Sopenharmony_cierr_clk_put: 13938c2ecf20Sopenharmony_ci clk_put(rj54n1->clk); 13948c2ecf20Sopenharmony_ci 13958c2ecf20Sopenharmony_cierr_free_ctrl: 13968c2ecf20Sopenharmony_ci v4l2_ctrl_handler_free(&rj54n1->hdl); 13978c2ecf20Sopenharmony_ci 13988c2ecf20Sopenharmony_ci return ret; 13998c2ecf20Sopenharmony_ci} 14008c2ecf20Sopenharmony_ci 14018c2ecf20Sopenharmony_cistatic int rj54n1_remove(struct i2c_client *client) 14028c2ecf20Sopenharmony_ci{ 14038c2ecf20Sopenharmony_ci struct rj54n1 *rj54n1 = to_rj54n1(client); 14048c2ecf20Sopenharmony_ci 14058c2ecf20Sopenharmony_ci if (rj54n1->enable_gpio) 14068c2ecf20Sopenharmony_ci gpiod_put(rj54n1->enable_gpio); 14078c2ecf20Sopenharmony_ci if (rj54n1->pwup_gpio) 14088c2ecf20Sopenharmony_ci gpiod_put(rj54n1->pwup_gpio); 14098c2ecf20Sopenharmony_ci 14108c2ecf20Sopenharmony_ci clk_put(rj54n1->clk); 14118c2ecf20Sopenharmony_ci v4l2_ctrl_handler_free(&rj54n1->hdl); 14128c2ecf20Sopenharmony_ci v4l2_async_unregister_subdev(&rj54n1->subdev); 14138c2ecf20Sopenharmony_ci 14148c2ecf20Sopenharmony_ci return 0; 14158c2ecf20Sopenharmony_ci} 14168c2ecf20Sopenharmony_ci 14178c2ecf20Sopenharmony_cistatic const struct i2c_device_id rj54n1_id[] = { 14188c2ecf20Sopenharmony_ci { "rj54n1cb0c", 0 }, 14198c2ecf20Sopenharmony_ci { } 14208c2ecf20Sopenharmony_ci}; 14218c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, rj54n1_id); 14228c2ecf20Sopenharmony_ci 14238c2ecf20Sopenharmony_cistatic struct i2c_driver rj54n1_i2c_driver = { 14248c2ecf20Sopenharmony_ci .driver = { 14258c2ecf20Sopenharmony_ci .name = "rj54n1cb0c", 14268c2ecf20Sopenharmony_ci }, 14278c2ecf20Sopenharmony_ci .probe = rj54n1_probe, 14288c2ecf20Sopenharmony_ci .remove = rj54n1_remove, 14298c2ecf20Sopenharmony_ci .id_table = rj54n1_id, 14308c2ecf20Sopenharmony_ci}; 14318c2ecf20Sopenharmony_ci 14328c2ecf20Sopenharmony_cimodule_i2c_driver(rj54n1_i2c_driver); 14338c2ecf20Sopenharmony_ci 14348c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Sharp RJ54N1CB0C Camera driver"); 14358c2ecf20Sopenharmony_ciMODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de>"); 14368c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 1437