18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Driver for Samsung S5K5BAF UXGA 1/5" 2M CMOS Image Sensor 48c2ecf20Sopenharmony_ci * with embedded SoC ISP. 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Copyright (C) 2013, Samsung Electronics Co., Ltd. 78c2ecf20Sopenharmony_ci * Andrzej Hajda <a.hajda@samsung.com> 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * Based on S5K6AA driver authored by Sylwester Nawrocki 108c2ecf20Sopenharmony_ci * Copyright (C) 2013, Samsung Electronics Co., Ltd. 118c2ecf20Sopenharmony_ci */ 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include <linux/clk.h> 148c2ecf20Sopenharmony_ci#include <linux/delay.h> 158c2ecf20Sopenharmony_ci#include <linux/firmware.h> 168c2ecf20Sopenharmony_ci#include <linux/gpio.h> 178c2ecf20Sopenharmony_ci#include <linux/i2c.h> 188c2ecf20Sopenharmony_ci#include <linux/media.h> 198c2ecf20Sopenharmony_ci#include <linux/module.h> 208c2ecf20Sopenharmony_ci#include <linux/of_gpio.h> 218c2ecf20Sopenharmony_ci#include <linux/of_graph.h> 228c2ecf20Sopenharmony_ci#include <linux/regulator/consumer.h> 238c2ecf20Sopenharmony_ci#include <linux/slab.h> 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#include <media/media-entity.h> 268c2ecf20Sopenharmony_ci#include <media/v4l2-ctrls.h> 278c2ecf20Sopenharmony_ci#include <media/v4l2-device.h> 288c2ecf20Sopenharmony_ci#include <media/v4l2-subdev.h> 298c2ecf20Sopenharmony_ci#include <media/v4l2-mediabus.h> 308c2ecf20Sopenharmony_ci#include <media/v4l2-fwnode.h> 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_cistatic int debug; 338c2ecf20Sopenharmony_cimodule_param(debug, int, 0644); 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci#define S5K5BAF_DRIVER_NAME "s5k5baf" 368c2ecf20Sopenharmony_ci#define S5K5BAF_DEFAULT_MCLK_FREQ 24000000U 378c2ecf20Sopenharmony_ci#define S5K5BAF_CLK_NAME "mclk" 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci#define S5K5BAF_FW_FILENAME "s5k5baf-cfg.bin" 408c2ecf20Sopenharmony_ci#define S5K5BAF_FW_TAG "SF00" 418c2ecf20Sopenharmony_ci#define S5K5BAG_FW_TAG_LEN 2 428c2ecf20Sopenharmony_ci#define S5K5BAG_FW_MAX_COUNT 16 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci#define S5K5BAF_CIS_WIDTH 1600 458c2ecf20Sopenharmony_ci#define S5K5BAF_CIS_HEIGHT 1200 468c2ecf20Sopenharmony_ci#define S5K5BAF_WIN_WIDTH_MIN 8 478c2ecf20Sopenharmony_ci#define S5K5BAF_WIN_HEIGHT_MIN 8 488c2ecf20Sopenharmony_ci#define S5K5BAF_GAIN_RED_DEF 127 498c2ecf20Sopenharmony_ci#define S5K5BAF_GAIN_GREEN_DEF 95 508c2ecf20Sopenharmony_ci#define S5K5BAF_GAIN_BLUE_DEF 180 518c2ecf20Sopenharmony_ci/* Default number of MIPI CSI-2 data lanes used */ 528c2ecf20Sopenharmony_ci#define S5K5BAF_DEF_NUM_LANES 1 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci#define AHB_MSB_ADDR_PTR 0xfcfc 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci/* 578c2ecf20Sopenharmony_ci * Register interface pages (the most significant word of the address) 588c2ecf20Sopenharmony_ci */ 598c2ecf20Sopenharmony_ci#define PAGE_IF_HW 0xd000 608c2ecf20Sopenharmony_ci#define PAGE_IF_SW 0x7000 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci/* 638c2ecf20Sopenharmony_ci * H/W register Interface (PAGE_IF_HW) 648c2ecf20Sopenharmony_ci */ 658c2ecf20Sopenharmony_ci#define REG_SW_LOAD_COMPLETE 0x0014 668c2ecf20Sopenharmony_ci#define REG_CMDWR_PAGE 0x0028 678c2ecf20Sopenharmony_ci#define REG_CMDWR_ADDR 0x002a 688c2ecf20Sopenharmony_ci#define REG_CMDRD_PAGE 0x002c 698c2ecf20Sopenharmony_ci#define REG_CMDRD_ADDR 0x002e 708c2ecf20Sopenharmony_ci#define REG_CMD_BUF 0x0f12 718c2ecf20Sopenharmony_ci#define REG_SET_HOST_INT 0x1000 728c2ecf20Sopenharmony_ci#define REG_CLEAR_HOST_INT 0x1030 738c2ecf20Sopenharmony_ci#define REG_PATTERN_SET 0x3100 748c2ecf20Sopenharmony_ci#define REG_PATTERN_WIDTH 0x3118 758c2ecf20Sopenharmony_ci#define REG_PATTERN_HEIGHT 0x311a 768c2ecf20Sopenharmony_ci#define REG_PATTERN_PARAM 0x311c 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci/* 798c2ecf20Sopenharmony_ci * S/W register interface (PAGE_IF_SW) 808c2ecf20Sopenharmony_ci */ 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci/* Firmware revision information */ 838c2ecf20Sopenharmony_ci#define REG_FW_APIVER 0x012e 848c2ecf20Sopenharmony_ci#define S5K5BAF_FW_APIVER 0x0001 858c2ecf20Sopenharmony_ci#define REG_FW_REVISION 0x0130 868c2ecf20Sopenharmony_ci#define REG_FW_SENSOR_ID 0x0152 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci/* Initialization parameters */ 898c2ecf20Sopenharmony_ci/* Master clock frequency in KHz */ 908c2ecf20Sopenharmony_ci#define REG_I_INCLK_FREQ_L 0x01b8 918c2ecf20Sopenharmony_ci#define REG_I_INCLK_FREQ_H 0x01ba 928c2ecf20Sopenharmony_ci#define MIN_MCLK_FREQ_KHZ 6000U 938c2ecf20Sopenharmony_ci#define MAX_MCLK_FREQ_KHZ 48000U 948c2ecf20Sopenharmony_ci#define REG_I_USE_NPVI_CLOCKS 0x01c6 958c2ecf20Sopenharmony_ci#define NPVI_CLOCKS 1 968c2ecf20Sopenharmony_ci#define REG_I_USE_NMIPI_CLOCKS 0x01c8 978c2ecf20Sopenharmony_ci#define NMIPI_CLOCKS 1 988c2ecf20Sopenharmony_ci#define REG_I_BLOCK_INTERNAL_PLL_CALC 0x01ca 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci/* Clock configurations, n = 0..2. REG_I_* frequency unit is 4 kHz. */ 1018c2ecf20Sopenharmony_ci#define REG_I_OPCLK_4KHZ(n) ((n) * 6 + 0x01cc) 1028c2ecf20Sopenharmony_ci#define REG_I_MIN_OUTRATE_4KHZ(n) ((n) * 6 + 0x01ce) 1038c2ecf20Sopenharmony_ci#define REG_I_MAX_OUTRATE_4KHZ(n) ((n) * 6 + 0x01d0) 1048c2ecf20Sopenharmony_ci#define SCLK_PVI_FREQ 24000 1058c2ecf20Sopenharmony_ci#define SCLK_MIPI_FREQ 48000 1068c2ecf20Sopenharmony_ci#define PCLK_MIN_FREQ 6000 1078c2ecf20Sopenharmony_ci#define PCLK_MAX_FREQ 48000 1088c2ecf20Sopenharmony_ci#define REG_I_USE_REGS_API 0x01de 1098c2ecf20Sopenharmony_ci#define REG_I_INIT_PARAMS_UPDATED 0x01e0 1108c2ecf20Sopenharmony_ci#define REG_I_ERROR_INFO 0x01e2 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci/* General purpose parameters */ 1138c2ecf20Sopenharmony_ci#define REG_USER_BRIGHTNESS 0x01e4 1148c2ecf20Sopenharmony_ci#define REG_USER_CONTRAST 0x01e6 1158c2ecf20Sopenharmony_ci#define REG_USER_SATURATION 0x01e8 1168c2ecf20Sopenharmony_ci#define REG_USER_SHARPBLUR 0x01ea 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci#define REG_G_SPEC_EFFECTS 0x01ee 1198c2ecf20Sopenharmony_ci#define REG_G_ENABLE_PREV 0x01f0 1208c2ecf20Sopenharmony_ci#define REG_G_ENABLE_PREV_CHG 0x01f2 1218c2ecf20Sopenharmony_ci#define REG_G_NEW_CFG_SYNC 0x01f8 1228c2ecf20Sopenharmony_ci#define REG_G_PREVREQ_IN_WIDTH 0x01fa 1238c2ecf20Sopenharmony_ci#define REG_G_PREVREQ_IN_HEIGHT 0x01fc 1248c2ecf20Sopenharmony_ci#define REG_G_PREVREQ_IN_XOFFS 0x01fe 1258c2ecf20Sopenharmony_ci#define REG_G_PREVREQ_IN_YOFFS 0x0200 1268c2ecf20Sopenharmony_ci#define REG_G_PREVZOOM_IN_WIDTH 0x020a 1278c2ecf20Sopenharmony_ci#define REG_G_PREVZOOM_IN_HEIGHT 0x020c 1288c2ecf20Sopenharmony_ci#define REG_G_PREVZOOM_IN_XOFFS 0x020e 1298c2ecf20Sopenharmony_ci#define REG_G_PREVZOOM_IN_YOFFS 0x0210 1308c2ecf20Sopenharmony_ci#define REG_G_INPUTS_CHANGE_REQ 0x021a 1318c2ecf20Sopenharmony_ci#define REG_G_ACTIVE_PREV_CFG 0x021c 1328c2ecf20Sopenharmony_ci#define REG_G_PREV_CFG_CHG 0x021e 1338c2ecf20Sopenharmony_ci#define REG_G_PREV_OPEN_AFTER_CH 0x0220 1348c2ecf20Sopenharmony_ci#define REG_G_PREV_CFG_ERROR 0x0222 1358c2ecf20Sopenharmony_ci#define CFG_ERROR_RANGE 0x0b 1368c2ecf20Sopenharmony_ci#define REG_G_PREV_CFG_BYPASS_CHANGED 0x022a 1378c2ecf20Sopenharmony_ci#define REG_G_ACTUAL_P_FR_TIME 0x023a 1388c2ecf20Sopenharmony_ci#define REG_G_ACTUAL_P_OUT_RATE 0x023c 1398c2ecf20Sopenharmony_ci#define REG_G_ACTUAL_C_FR_TIME 0x023e 1408c2ecf20Sopenharmony_ci#define REG_G_ACTUAL_C_OUT_RATE 0x0240 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci/* Preview control section. n = 0...4. */ 1438c2ecf20Sopenharmony_ci#define PREG(n, x) ((n) * 0x26 + x) 1448c2ecf20Sopenharmony_ci#define REG_P_OUT_WIDTH(n) PREG(n, 0x0242) 1458c2ecf20Sopenharmony_ci#define REG_P_OUT_HEIGHT(n) PREG(n, 0x0244) 1468c2ecf20Sopenharmony_ci#define REG_P_FMT(n) PREG(n, 0x0246) 1478c2ecf20Sopenharmony_ci#define REG_P_MAX_OUT_RATE(n) PREG(n, 0x0248) 1488c2ecf20Sopenharmony_ci#define REG_P_MIN_OUT_RATE(n) PREG(n, 0x024a) 1498c2ecf20Sopenharmony_ci#define REG_P_PVI_MASK(n) PREG(n, 0x024c) 1508c2ecf20Sopenharmony_ci#define PVI_MASK_MIPI 0x52 1518c2ecf20Sopenharmony_ci#define REG_P_CLK_INDEX(n) PREG(n, 0x024e) 1528c2ecf20Sopenharmony_ci#define CLK_PVI_INDEX 0 1538c2ecf20Sopenharmony_ci#define CLK_MIPI_INDEX NPVI_CLOCKS 1548c2ecf20Sopenharmony_ci#define REG_P_FR_RATE_TYPE(n) PREG(n, 0x0250) 1558c2ecf20Sopenharmony_ci#define FR_RATE_DYNAMIC 0 1568c2ecf20Sopenharmony_ci#define FR_RATE_FIXED 1 1578c2ecf20Sopenharmony_ci#define FR_RATE_FIXED_ACCURATE 2 1588c2ecf20Sopenharmony_ci#define REG_P_FR_RATE_Q_TYPE(n) PREG(n, 0x0252) 1598c2ecf20Sopenharmony_ci#define FR_RATE_Q_DYNAMIC 0 1608c2ecf20Sopenharmony_ci#define FR_RATE_Q_BEST_FRRATE 1 /* Binning enabled */ 1618c2ecf20Sopenharmony_ci#define FR_RATE_Q_BEST_QUALITY 2 /* Binning disabled */ 1628c2ecf20Sopenharmony_ci/* Frame period in 0.1 ms units */ 1638c2ecf20Sopenharmony_ci#define REG_P_MAX_FR_TIME(n) PREG(n, 0x0254) 1648c2ecf20Sopenharmony_ci#define REG_P_MIN_FR_TIME(n) PREG(n, 0x0256) 1658c2ecf20Sopenharmony_ci#define S5K5BAF_MIN_FR_TIME 333 /* x100 us */ 1668c2ecf20Sopenharmony_ci#define S5K5BAF_MAX_FR_TIME 6500 /* x100 us */ 1678c2ecf20Sopenharmony_ci/* The below 5 registers are for "device correction" values */ 1688c2ecf20Sopenharmony_ci#define REG_P_SATURATION(n) PREG(n, 0x0258) 1698c2ecf20Sopenharmony_ci#define REG_P_SHARP_BLUR(n) PREG(n, 0x025a) 1708c2ecf20Sopenharmony_ci#define REG_P_GLAMOUR(n) PREG(n, 0x025c) 1718c2ecf20Sopenharmony_ci#define REG_P_COLORTEMP(n) PREG(n, 0x025e) 1728c2ecf20Sopenharmony_ci#define REG_P_GAMMA_INDEX(n) PREG(n, 0x0260) 1738c2ecf20Sopenharmony_ci#define REG_P_PREV_MIRROR(n) PREG(n, 0x0262) 1748c2ecf20Sopenharmony_ci#define REG_P_CAP_MIRROR(n) PREG(n, 0x0264) 1758c2ecf20Sopenharmony_ci#define REG_P_CAP_ROTATION(n) PREG(n, 0x0266) 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci/* Extended image property controls */ 1788c2ecf20Sopenharmony_ci/* Exposure time in 10 us units */ 1798c2ecf20Sopenharmony_ci#define REG_SF_USR_EXPOSURE_L 0x03bc 1808c2ecf20Sopenharmony_ci#define REG_SF_USR_EXPOSURE_H 0x03be 1818c2ecf20Sopenharmony_ci#define REG_SF_USR_EXPOSURE_CHG 0x03c0 1828c2ecf20Sopenharmony_ci#define REG_SF_USR_TOT_GAIN 0x03c2 1838c2ecf20Sopenharmony_ci#define REG_SF_USR_TOT_GAIN_CHG 0x03c4 1848c2ecf20Sopenharmony_ci#define REG_SF_RGAIN 0x03c6 1858c2ecf20Sopenharmony_ci#define REG_SF_RGAIN_CHG 0x03c8 1868c2ecf20Sopenharmony_ci#define REG_SF_GGAIN 0x03ca 1878c2ecf20Sopenharmony_ci#define REG_SF_GGAIN_CHG 0x03cc 1888c2ecf20Sopenharmony_ci#define REG_SF_BGAIN 0x03ce 1898c2ecf20Sopenharmony_ci#define REG_SF_BGAIN_CHG 0x03d0 1908c2ecf20Sopenharmony_ci#define REG_SF_WBGAIN_CHG 0x03d2 1918c2ecf20Sopenharmony_ci#define REG_SF_FLICKER_QUANT 0x03d4 1928c2ecf20Sopenharmony_ci#define REG_SF_FLICKER_QUANT_CHG 0x03d6 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci/* Output interface (parallel/MIPI) setup */ 1958c2ecf20Sopenharmony_ci#define REG_OIF_EN_MIPI_LANES 0x03f2 1968c2ecf20Sopenharmony_ci#define REG_OIF_EN_PACKETS 0x03f4 1978c2ecf20Sopenharmony_ci#define EN_PACKETS_CSI2 0xc3 1988c2ecf20Sopenharmony_ci#define REG_OIF_CFG_CHG 0x03f6 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci/* Auto-algorithms enable mask */ 2018c2ecf20Sopenharmony_ci#define REG_DBG_AUTOALG_EN 0x03f8 2028c2ecf20Sopenharmony_ci#define AALG_ALL_EN BIT(0) 2038c2ecf20Sopenharmony_ci#define AALG_AE_EN BIT(1) 2048c2ecf20Sopenharmony_ci#define AALG_DIVLEI_EN BIT(2) 2058c2ecf20Sopenharmony_ci#define AALG_WB_EN BIT(3) 2068c2ecf20Sopenharmony_ci#define AALG_USE_WB_FOR_ISP BIT(4) 2078c2ecf20Sopenharmony_ci#define AALG_FLICKER_EN BIT(5) 2088c2ecf20Sopenharmony_ci#define AALG_FIT_EN BIT(6) 2098c2ecf20Sopenharmony_ci#define AALG_WRHW_EN BIT(7) 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci/* Pointers to color correction matrices */ 2128c2ecf20Sopenharmony_ci#define REG_PTR_CCM_HORIZON 0x06d0 2138c2ecf20Sopenharmony_ci#define REG_PTR_CCM_INCANDESCENT 0x06d4 2148c2ecf20Sopenharmony_ci#define REG_PTR_CCM_WARM_WHITE 0x06d8 2158c2ecf20Sopenharmony_ci#define REG_PTR_CCM_COOL_WHITE 0x06dc 2168c2ecf20Sopenharmony_ci#define REG_PTR_CCM_DL50 0x06e0 2178c2ecf20Sopenharmony_ci#define REG_PTR_CCM_DL65 0x06e4 2188c2ecf20Sopenharmony_ci#define REG_PTR_CCM_OUTDOOR 0x06ec 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci#define REG_ARR_CCM(n) (0x2800 + 36 * (n)) 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_cistatic const char * const s5k5baf_supply_names[] = { 2238c2ecf20Sopenharmony_ci "vdda", /* Analog power supply 2.8V (2.6V to 3.0V) */ 2248c2ecf20Sopenharmony_ci "vddreg", /* Regulator input power supply 1.8V (1.7V to 1.9V) 2258c2ecf20Sopenharmony_ci or 2.8V (2.6V to 3.0) */ 2268c2ecf20Sopenharmony_ci "vddio", /* I/O power supply 1.8V (1.65V to 1.95V) 2278c2ecf20Sopenharmony_ci or 2.8V (2.5V to 3.1V) */ 2288c2ecf20Sopenharmony_ci}; 2298c2ecf20Sopenharmony_ci#define S5K5BAF_NUM_SUPPLIES ARRAY_SIZE(s5k5baf_supply_names) 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_cistruct s5k5baf_gpio { 2328c2ecf20Sopenharmony_ci int gpio; 2338c2ecf20Sopenharmony_ci int level; 2348c2ecf20Sopenharmony_ci}; 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_cienum s5k5baf_gpio_id { 2378c2ecf20Sopenharmony_ci STBY, 2388c2ecf20Sopenharmony_ci RSET, 2398c2ecf20Sopenharmony_ci NUM_GPIOS, 2408c2ecf20Sopenharmony_ci}; 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci#define PAD_CIS 0 2438c2ecf20Sopenharmony_ci#define PAD_OUT 1 2448c2ecf20Sopenharmony_ci#define NUM_CIS_PADS 1 2458c2ecf20Sopenharmony_ci#define NUM_ISP_PADS 2 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_cistruct s5k5baf_pixfmt { 2488c2ecf20Sopenharmony_ci u32 code; 2498c2ecf20Sopenharmony_ci u32 colorspace; 2508c2ecf20Sopenharmony_ci /* REG_P_FMT(x) register value */ 2518c2ecf20Sopenharmony_ci u16 reg_p_fmt; 2528c2ecf20Sopenharmony_ci}; 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_cistruct s5k5baf_ctrls { 2558c2ecf20Sopenharmony_ci struct v4l2_ctrl_handler handler; 2568c2ecf20Sopenharmony_ci struct { /* Auto / manual white balance cluster */ 2578c2ecf20Sopenharmony_ci struct v4l2_ctrl *awb; 2588c2ecf20Sopenharmony_ci struct v4l2_ctrl *gain_red; 2598c2ecf20Sopenharmony_ci struct v4l2_ctrl *gain_blue; 2608c2ecf20Sopenharmony_ci }; 2618c2ecf20Sopenharmony_ci struct { /* Mirror cluster */ 2628c2ecf20Sopenharmony_ci struct v4l2_ctrl *hflip; 2638c2ecf20Sopenharmony_ci struct v4l2_ctrl *vflip; 2648c2ecf20Sopenharmony_ci }; 2658c2ecf20Sopenharmony_ci struct { /* Auto exposure / manual exposure and gain cluster */ 2668c2ecf20Sopenharmony_ci struct v4l2_ctrl *auto_exp; 2678c2ecf20Sopenharmony_ci struct v4l2_ctrl *exposure; 2688c2ecf20Sopenharmony_ci struct v4l2_ctrl *gain; 2698c2ecf20Sopenharmony_ci }; 2708c2ecf20Sopenharmony_ci}; 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_cienum { 2738c2ecf20Sopenharmony_ci S5K5BAF_FW_ID_PATCH, 2748c2ecf20Sopenharmony_ci S5K5BAF_FW_ID_CCM, 2758c2ecf20Sopenharmony_ci S5K5BAF_FW_ID_CIS, 2768c2ecf20Sopenharmony_ci}; 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_cistruct s5k5baf_fw { 2798c2ecf20Sopenharmony_ci u16 count; 2808c2ecf20Sopenharmony_ci struct { 2818c2ecf20Sopenharmony_ci u16 id; 2828c2ecf20Sopenharmony_ci u16 offset; 2838c2ecf20Sopenharmony_ci } seq[]; 2848c2ecf20Sopenharmony_ci}; 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_cistruct s5k5baf { 2878c2ecf20Sopenharmony_ci struct s5k5baf_gpio gpios[NUM_GPIOS]; 2888c2ecf20Sopenharmony_ci enum v4l2_mbus_type bus_type; 2898c2ecf20Sopenharmony_ci u8 nlanes; 2908c2ecf20Sopenharmony_ci struct regulator_bulk_data supplies[S5K5BAF_NUM_SUPPLIES]; 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci struct clk *clock; 2938c2ecf20Sopenharmony_ci u32 mclk_frequency; 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci struct s5k5baf_fw *fw; 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci struct v4l2_subdev cis_sd; 2988c2ecf20Sopenharmony_ci struct media_pad cis_pad; 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci struct v4l2_subdev sd; 3018c2ecf20Sopenharmony_ci struct media_pad pads[NUM_ISP_PADS]; 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci /* protects the struct members below */ 3048c2ecf20Sopenharmony_ci struct mutex lock; 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci int error; 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci struct v4l2_rect crop_sink; 3098c2ecf20Sopenharmony_ci struct v4l2_rect compose; 3108c2ecf20Sopenharmony_ci struct v4l2_rect crop_source; 3118c2ecf20Sopenharmony_ci /* index to s5k5baf_formats array */ 3128c2ecf20Sopenharmony_ci int pixfmt; 3138c2ecf20Sopenharmony_ci /* actual frame interval in 100us */ 3148c2ecf20Sopenharmony_ci u16 fiv; 3158c2ecf20Sopenharmony_ci /* requested frame interval in 100us */ 3168c2ecf20Sopenharmony_ci u16 req_fiv; 3178c2ecf20Sopenharmony_ci /* cache for REG_DBG_AUTOALG_EN register */ 3188c2ecf20Sopenharmony_ci u16 auto_alg; 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci struct s5k5baf_ctrls ctrls; 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci unsigned int streaming:1; 3238c2ecf20Sopenharmony_ci unsigned int apply_cfg:1; 3248c2ecf20Sopenharmony_ci unsigned int apply_crop:1; 3258c2ecf20Sopenharmony_ci unsigned int valid_auto_alg:1; 3268c2ecf20Sopenharmony_ci unsigned int power; 3278c2ecf20Sopenharmony_ci}; 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_cistatic const struct s5k5baf_pixfmt s5k5baf_formats[] = { 3308c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_VYUY8_2X8, V4L2_COLORSPACE_JPEG, 5 }, 3318c2ecf20Sopenharmony_ci /* range 16-240 */ 3328c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_VYUY8_2X8, V4L2_COLORSPACE_REC709, 6 }, 3338c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_RGB565_2X8_BE, V4L2_COLORSPACE_JPEG, 0 }, 3348c2ecf20Sopenharmony_ci}; 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_cistatic struct v4l2_rect s5k5baf_cis_rect = { 3378c2ecf20Sopenharmony_ci 0, 0, S5K5BAF_CIS_WIDTH, S5K5BAF_CIS_HEIGHT 3388c2ecf20Sopenharmony_ci}; 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci/* Setfile contains set of I2C command sequences. Each sequence has its ID. 3418c2ecf20Sopenharmony_ci * setfile format: 3428c2ecf20Sopenharmony_ci * u8 magic[4]; 3438c2ecf20Sopenharmony_ci * u16 count; number of sequences 3448c2ecf20Sopenharmony_ci * struct { 3458c2ecf20Sopenharmony_ci * u16 id; sequence id 3468c2ecf20Sopenharmony_ci * u16 offset; sequence offset in data array 3478c2ecf20Sopenharmony_ci * } seq[count]; 3488c2ecf20Sopenharmony_ci * u16 data[*]; array containing sequences 3498c2ecf20Sopenharmony_ci * 3508c2ecf20Sopenharmony_ci */ 3518c2ecf20Sopenharmony_cistatic int s5k5baf_fw_parse(struct device *dev, struct s5k5baf_fw **fw, 3528c2ecf20Sopenharmony_ci size_t count, const __le16 *data) 3538c2ecf20Sopenharmony_ci{ 3548c2ecf20Sopenharmony_ci struct s5k5baf_fw *f; 3558c2ecf20Sopenharmony_ci u16 *d, i, *end; 3568c2ecf20Sopenharmony_ci int ret; 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci if (count < S5K5BAG_FW_TAG_LEN + 1) { 3598c2ecf20Sopenharmony_ci dev_err(dev, "firmware file too short (%zu)\n", count); 3608c2ecf20Sopenharmony_ci return -EINVAL; 3618c2ecf20Sopenharmony_ci } 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci ret = memcmp(data, S5K5BAF_FW_TAG, S5K5BAG_FW_TAG_LEN * sizeof(u16)); 3648c2ecf20Sopenharmony_ci if (ret != 0) { 3658c2ecf20Sopenharmony_ci dev_err(dev, "invalid firmware magic number\n"); 3668c2ecf20Sopenharmony_ci return -EINVAL; 3678c2ecf20Sopenharmony_ci } 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci data += S5K5BAG_FW_TAG_LEN; 3708c2ecf20Sopenharmony_ci count -= S5K5BAG_FW_TAG_LEN; 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci d = devm_kcalloc(dev, count, sizeof(u16), GFP_KERNEL); 3738c2ecf20Sopenharmony_ci if (!d) 3748c2ecf20Sopenharmony_ci return -ENOMEM; 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci for (i = 0; i < count; ++i) 3778c2ecf20Sopenharmony_ci d[i] = le16_to_cpu(data[i]); 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci f = (struct s5k5baf_fw *)d; 3808c2ecf20Sopenharmony_ci if (count < 1 + 2 * f->count) { 3818c2ecf20Sopenharmony_ci dev_err(dev, "invalid firmware header (count=%d size=%zu)\n", 3828c2ecf20Sopenharmony_ci f->count, 2 * (count + S5K5BAG_FW_TAG_LEN)); 3838c2ecf20Sopenharmony_ci return -EINVAL; 3848c2ecf20Sopenharmony_ci } 3858c2ecf20Sopenharmony_ci end = d + count; 3868c2ecf20Sopenharmony_ci d += 1 + 2 * f->count; 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci for (i = 0; i < f->count; ++i) { 3898c2ecf20Sopenharmony_ci if (f->seq[i].offset + d <= end) 3908c2ecf20Sopenharmony_ci continue; 3918c2ecf20Sopenharmony_ci dev_err(dev, "invalid firmware header (seq=%d)\n", i); 3928c2ecf20Sopenharmony_ci return -EINVAL; 3938c2ecf20Sopenharmony_ci } 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci *fw = f; 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci return 0; 3988c2ecf20Sopenharmony_ci} 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_cistatic inline struct v4l2_subdev *ctrl_to_sd(struct v4l2_ctrl *ctrl) 4018c2ecf20Sopenharmony_ci{ 4028c2ecf20Sopenharmony_ci return &container_of(ctrl->handler, struct s5k5baf, ctrls.handler)->sd; 4038c2ecf20Sopenharmony_ci} 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_cistatic inline bool s5k5baf_is_cis_subdev(struct v4l2_subdev *sd) 4068c2ecf20Sopenharmony_ci{ 4078c2ecf20Sopenharmony_ci return sd->entity.function == MEDIA_ENT_F_CAM_SENSOR; 4088c2ecf20Sopenharmony_ci} 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_cistatic inline struct s5k5baf *to_s5k5baf(struct v4l2_subdev *sd) 4118c2ecf20Sopenharmony_ci{ 4128c2ecf20Sopenharmony_ci if (s5k5baf_is_cis_subdev(sd)) 4138c2ecf20Sopenharmony_ci return container_of(sd, struct s5k5baf, cis_sd); 4148c2ecf20Sopenharmony_ci else 4158c2ecf20Sopenharmony_ci return container_of(sd, struct s5k5baf, sd); 4168c2ecf20Sopenharmony_ci} 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_cistatic u16 s5k5baf_i2c_read(struct s5k5baf *state, u16 addr) 4198c2ecf20Sopenharmony_ci{ 4208c2ecf20Sopenharmony_ci struct i2c_client *c = v4l2_get_subdevdata(&state->sd); 4218c2ecf20Sopenharmony_ci __be16 w, r; 4228c2ecf20Sopenharmony_ci u16 res; 4238c2ecf20Sopenharmony_ci struct i2c_msg msg[] = { 4248c2ecf20Sopenharmony_ci { .addr = c->addr, .flags = 0, 4258c2ecf20Sopenharmony_ci .len = 2, .buf = (u8 *)&w }, 4268c2ecf20Sopenharmony_ci { .addr = c->addr, .flags = I2C_M_RD, 4278c2ecf20Sopenharmony_ci .len = 2, .buf = (u8 *)&r }, 4288c2ecf20Sopenharmony_ci }; 4298c2ecf20Sopenharmony_ci int ret; 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci if (state->error) 4328c2ecf20Sopenharmony_ci return 0; 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci w = cpu_to_be16(addr); 4358c2ecf20Sopenharmony_ci ret = i2c_transfer(c->adapter, msg, 2); 4368c2ecf20Sopenharmony_ci res = be16_to_cpu(r); 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci v4l2_dbg(3, debug, c, "i2c_read: 0x%04x : 0x%04x\n", addr, res); 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci if (ret != 2) { 4418c2ecf20Sopenharmony_ci v4l2_err(c, "i2c_read: error during transfer (%d)\n", ret); 4428c2ecf20Sopenharmony_ci state->error = ret; 4438c2ecf20Sopenharmony_ci } 4448c2ecf20Sopenharmony_ci return res; 4458c2ecf20Sopenharmony_ci} 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_cistatic void s5k5baf_i2c_write(struct s5k5baf *state, u16 addr, u16 val) 4488c2ecf20Sopenharmony_ci{ 4498c2ecf20Sopenharmony_ci u8 buf[4] = { addr >> 8, addr & 0xFF, val >> 8, val & 0xFF }; 4508c2ecf20Sopenharmony_ci struct i2c_client *c = v4l2_get_subdevdata(&state->sd); 4518c2ecf20Sopenharmony_ci int ret; 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci if (state->error) 4548c2ecf20Sopenharmony_ci return; 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci ret = i2c_master_send(c, buf, 4); 4578c2ecf20Sopenharmony_ci v4l2_dbg(3, debug, c, "i2c_write: 0x%04x : 0x%04x\n", addr, val); 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci if (ret != 4) { 4608c2ecf20Sopenharmony_ci v4l2_err(c, "i2c_write: error during transfer (%d)\n", ret); 4618c2ecf20Sopenharmony_ci state->error = ret; 4628c2ecf20Sopenharmony_ci } 4638c2ecf20Sopenharmony_ci} 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_cistatic u16 s5k5baf_read(struct s5k5baf *state, u16 addr) 4668c2ecf20Sopenharmony_ci{ 4678c2ecf20Sopenharmony_ci s5k5baf_i2c_write(state, REG_CMDRD_ADDR, addr); 4688c2ecf20Sopenharmony_ci return s5k5baf_i2c_read(state, REG_CMD_BUF); 4698c2ecf20Sopenharmony_ci} 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_cistatic void s5k5baf_write(struct s5k5baf *state, u16 addr, u16 val) 4728c2ecf20Sopenharmony_ci{ 4738c2ecf20Sopenharmony_ci s5k5baf_i2c_write(state, REG_CMDWR_ADDR, addr); 4748c2ecf20Sopenharmony_ci s5k5baf_i2c_write(state, REG_CMD_BUF, val); 4758c2ecf20Sopenharmony_ci} 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_cistatic void s5k5baf_write_arr_seq(struct s5k5baf *state, u16 addr, 4788c2ecf20Sopenharmony_ci u16 count, const u16 *seq) 4798c2ecf20Sopenharmony_ci{ 4808c2ecf20Sopenharmony_ci struct i2c_client *c = v4l2_get_subdevdata(&state->sd); 4818c2ecf20Sopenharmony_ci __be16 buf[65]; 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci s5k5baf_i2c_write(state, REG_CMDWR_ADDR, addr); 4848c2ecf20Sopenharmony_ci if (state->error) 4858c2ecf20Sopenharmony_ci return; 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci v4l2_dbg(3, debug, c, "i2c_write_seq(count=%d): %*ph\n", count, 4888c2ecf20Sopenharmony_ci min(2 * count, 64), seq); 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci buf[0] = cpu_to_be16(REG_CMD_BUF); 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ci while (count > 0) { 4938c2ecf20Sopenharmony_ci int n = min_t(int, count, ARRAY_SIZE(buf) - 1); 4948c2ecf20Sopenharmony_ci int ret, i; 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci for (i = 1; i <= n; ++i) 4978c2ecf20Sopenharmony_ci buf[i] = cpu_to_be16(*seq++); 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci i *= 2; 5008c2ecf20Sopenharmony_ci ret = i2c_master_send(c, (char *)buf, i); 5018c2ecf20Sopenharmony_ci if (ret != i) { 5028c2ecf20Sopenharmony_ci v4l2_err(c, "i2c_write_seq: error during transfer (%d)\n", ret); 5038c2ecf20Sopenharmony_ci state->error = ret; 5048c2ecf20Sopenharmony_ci break; 5058c2ecf20Sopenharmony_ci } 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_ci count -= n; 5088c2ecf20Sopenharmony_ci } 5098c2ecf20Sopenharmony_ci} 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci#define s5k5baf_write_seq(state, addr, seq...) \ 5128c2ecf20Sopenharmony_ci s5k5baf_write_arr_seq(state, addr, sizeof((char[]){ seq }), \ 5138c2ecf20Sopenharmony_ci (const u16 []){ seq }); 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci/* add items count at the beginning of the list */ 5168c2ecf20Sopenharmony_ci#define NSEQ(seq...) sizeof((char[]){ seq }), seq 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci/* 5198c2ecf20Sopenharmony_ci * s5k5baf_write_nseq() - Writes sequences of values to sensor memory via i2c 5208c2ecf20Sopenharmony_ci * @nseq: sequence of u16 words in format: 5218c2ecf20Sopenharmony_ci * (N, address, value[1]...value[N-1])*,0 5228c2ecf20Sopenharmony_ci * Ex.: 5238c2ecf20Sopenharmony_ci * u16 seq[] = { NSEQ(0x4000, 1, 1), NSEQ(0x4010, 640, 480), 0 }; 5248c2ecf20Sopenharmony_ci * ret = s5k5baf_write_nseq(c, seq); 5258c2ecf20Sopenharmony_ci */ 5268c2ecf20Sopenharmony_cistatic void s5k5baf_write_nseq(struct s5k5baf *state, const u16 *nseq) 5278c2ecf20Sopenharmony_ci{ 5288c2ecf20Sopenharmony_ci int count; 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ci while ((count = *nseq++)) { 5318c2ecf20Sopenharmony_ci u16 addr = *nseq++; 5328c2ecf20Sopenharmony_ci --count; 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ci s5k5baf_write_arr_seq(state, addr, count, nseq); 5358c2ecf20Sopenharmony_ci nseq += count; 5368c2ecf20Sopenharmony_ci } 5378c2ecf20Sopenharmony_ci} 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_cistatic void s5k5baf_synchronize(struct s5k5baf *state, int timeout, u16 addr) 5408c2ecf20Sopenharmony_ci{ 5418c2ecf20Sopenharmony_ci unsigned long end = jiffies + msecs_to_jiffies(timeout); 5428c2ecf20Sopenharmony_ci u16 reg; 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci s5k5baf_write(state, addr, 1); 5458c2ecf20Sopenharmony_ci do { 5468c2ecf20Sopenharmony_ci reg = s5k5baf_read(state, addr); 5478c2ecf20Sopenharmony_ci if (state->error || !reg) 5488c2ecf20Sopenharmony_ci return; 5498c2ecf20Sopenharmony_ci usleep_range(5000, 10000); 5508c2ecf20Sopenharmony_ci } while (time_is_after_jiffies(end)); 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_ci v4l2_err(&state->sd, "timeout on register synchronize (%#x)\n", addr); 5538c2ecf20Sopenharmony_ci state->error = -ETIMEDOUT; 5548c2ecf20Sopenharmony_ci} 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_cistatic u16 *s5k5baf_fw_get_seq(struct s5k5baf *state, u16 seq_id) 5578c2ecf20Sopenharmony_ci{ 5588c2ecf20Sopenharmony_ci struct s5k5baf_fw *fw = state->fw; 5598c2ecf20Sopenharmony_ci u16 *data; 5608c2ecf20Sopenharmony_ci int i; 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_ci if (fw == NULL) 5638c2ecf20Sopenharmony_ci return NULL; 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ci data = &fw->seq[0].id + 2 * fw->count; 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_ci for (i = 0; i < fw->count; ++i) { 5688c2ecf20Sopenharmony_ci if (fw->seq[i].id == seq_id) 5698c2ecf20Sopenharmony_ci return data + fw->seq[i].offset; 5708c2ecf20Sopenharmony_ci } 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_ci return NULL; 5738c2ecf20Sopenharmony_ci} 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_cistatic void s5k5baf_hw_patch(struct s5k5baf *state) 5768c2ecf20Sopenharmony_ci{ 5778c2ecf20Sopenharmony_ci u16 *seq = s5k5baf_fw_get_seq(state, S5K5BAF_FW_ID_PATCH); 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_ci if (seq) 5808c2ecf20Sopenharmony_ci s5k5baf_write_nseq(state, seq); 5818c2ecf20Sopenharmony_ci} 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_cistatic void s5k5baf_hw_set_clocks(struct s5k5baf *state) 5848c2ecf20Sopenharmony_ci{ 5858c2ecf20Sopenharmony_ci unsigned long mclk = state->mclk_frequency / 1000; 5868c2ecf20Sopenharmony_ci u16 status; 5878c2ecf20Sopenharmony_ci static const u16 nseq_clk_cfg[] = { 5888c2ecf20Sopenharmony_ci NSEQ(REG_I_USE_NPVI_CLOCKS, 5898c2ecf20Sopenharmony_ci NPVI_CLOCKS, NMIPI_CLOCKS, 0, 5908c2ecf20Sopenharmony_ci SCLK_PVI_FREQ / 4, PCLK_MIN_FREQ / 4, PCLK_MAX_FREQ / 4, 5918c2ecf20Sopenharmony_ci SCLK_MIPI_FREQ / 4, PCLK_MIN_FREQ / 4, PCLK_MAX_FREQ / 4), 5928c2ecf20Sopenharmony_ci NSEQ(REG_I_USE_REGS_API, 1), 5938c2ecf20Sopenharmony_ci 0 5948c2ecf20Sopenharmony_ci }; 5958c2ecf20Sopenharmony_ci 5968c2ecf20Sopenharmony_ci s5k5baf_write_seq(state, REG_I_INCLK_FREQ_L, mclk & 0xffff, mclk >> 16); 5978c2ecf20Sopenharmony_ci s5k5baf_write_nseq(state, nseq_clk_cfg); 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_ci s5k5baf_synchronize(state, 250, REG_I_INIT_PARAMS_UPDATED); 6008c2ecf20Sopenharmony_ci status = s5k5baf_read(state, REG_I_ERROR_INFO); 6018c2ecf20Sopenharmony_ci if (!state->error && status) { 6028c2ecf20Sopenharmony_ci v4l2_err(&state->sd, "error configuring PLL (%d)\n", status); 6038c2ecf20Sopenharmony_ci state->error = -EINVAL; 6048c2ecf20Sopenharmony_ci } 6058c2ecf20Sopenharmony_ci} 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_ci/* set custom color correction matrices for various illuminations */ 6088c2ecf20Sopenharmony_cistatic void s5k5baf_hw_set_ccm(struct s5k5baf *state) 6098c2ecf20Sopenharmony_ci{ 6108c2ecf20Sopenharmony_ci u16 *seq = s5k5baf_fw_get_seq(state, S5K5BAF_FW_ID_CCM); 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_ci if (seq) 6138c2ecf20Sopenharmony_ci s5k5baf_write_nseq(state, seq); 6148c2ecf20Sopenharmony_ci} 6158c2ecf20Sopenharmony_ci 6168c2ecf20Sopenharmony_ci/* CIS sensor tuning, based on undocumented android driver code */ 6178c2ecf20Sopenharmony_cistatic void s5k5baf_hw_set_cis(struct s5k5baf *state) 6188c2ecf20Sopenharmony_ci{ 6198c2ecf20Sopenharmony_ci u16 *seq = s5k5baf_fw_get_seq(state, S5K5BAF_FW_ID_CIS); 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_ci if (!seq) 6228c2ecf20Sopenharmony_ci return; 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_ci s5k5baf_i2c_write(state, REG_CMDWR_PAGE, PAGE_IF_HW); 6258c2ecf20Sopenharmony_ci s5k5baf_write_nseq(state, seq); 6268c2ecf20Sopenharmony_ci s5k5baf_i2c_write(state, REG_CMDWR_PAGE, PAGE_IF_SW); 6278c2ecf20Sopenharmony_ci} 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_cistatic void s5k5baf_hw_sync_cfg(struct s5k5baf *state) 6308c2ecf20Sopenharmony_ci{ 6318c2ecf20Sopenharmony_ci s5k5baf_write(state, REG_G_PREV_CFG_CHG, 1); 6328c2ecf20Sopenharmony_ci if (state->apply_crop) { 6338c2ecf20Sopenharmony_ci s5k5baf_write(state, REG_G_INPUTS_CHANGE_REQ, 1); 6348c2ecf20Sopenharmony_ci s5k5baf_write(state, REG_G_PREV_CFG_BYPASS_CHANGED, 1); 6358c2ecf20Sopenharmony_ci } 6368c2ecf20Sopenharmony_ci s5k5baf_synchronize(state, 500, REG_G_NEW_CFG_SYNC); 6378c2ecf20Sopenharmony_ci} 6388c2ecf20Sopenharmony_ci/* Set horizontal and vertical image flipping */ 6398c2ecf20Sopenharmony_cistatic void s5k5baf_hw_set_mirror(struct s5k5baf *state) 6408c2ecf20Sopenharmony_ci{ 6418c2ecf20Sopenharmony_ci u16 flip = state->ctrls.vflip->val | (state->ctrls.vflip->val << 1); 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_ci s5k5baf_write(state, REG_P_PREV_MIRROR(0), flip); 6448c2ecf20Sopenharmony_ci if (state->streaming) 6458c2ecf20Sopenharmony_ci s5k5baf_hw_sync_cfg(state); 6468c2ecf20Sopenharmony_ci} 6478c2ecf20Sopenharmony_ci 6488c2ecf20Sopenharmony_cistatic void s5k5baf_hw_set_alg(struct s5k5baf *state, u16 alg, bool enable) 6498c2ecf20Sopenharmony_ci{ 6508c2ecf20Sopenharmony_ci u16 cur_alg, new_alg; 6518c2ecf20Sopenharmony_ci 6528c2ecf20Sopenharmony_ci if (!state->valid_auto_alg) 6538c2ecf20Sopenharmony_ci cur_alg = s5k5baf_read(state, REG_DBG_AUTOALG_EN); 6548c2ecf20Sopenharmony_ci else 6558c2ecf20Sopenharmony_ci cur_alg = state->auto_alg; 6568c2ecf20Sopenharmony_ci 6578c2ecf20Sopenharmony_ci new_alg = enable ? (cur_alg | alg) : (cur_alg & ~alg); 6588c2ecf20Sopenharmony_ci 6598c2ecf20Sopenharmony_ci if (new_alg != cur_alg) 6608c2ecf20Sopenharmony_ci s5k5baf_write(state, REG_DBG_AUTOALG_EN, new_alg); 6618c2ecf20Sopenharmony_ci 6628c2ecf20Sopenharmony_ci if (state->error) 6638c2ecf20Sopenharmony_ci return; 6648c2ecf20Sopenharmony_ci 6658c2ecf20Sopenharmony_ci state->valid_auto_alg = 1; 6668c2ecf20Sopenharmony_ci state->auto_alg = new_alg; 6678c2ecf20Sopenharmony_ci} 6688c2ecf20Sopenharmony_ci 6698c2ecf20Sopenharmony_ci/* Configure auto/manual white balance and R/G/B gains */ 6708c2ecf20Sopenharmony_cistatic void s5k5baf_hw_set_awb(struct s5k5baf *state, int awb) 6718c2ecf20Sopenharmony_ci{ 6728c2ecf20Sopenharmony_ci struct s5k5baf_ctrls *ctrls = &state->ctrls; 6738c2ecf20Sopenharmony_ci 6748c2ecf20Sopenharmony_ci if (!awb) 6758c2ecf20Sopenharmony_ci s5k5baf_write_seq(state, REG_SF_RGAIN, 6768c2ecf20Sopenharmony_ci ctrls->gain_red->val, 1, 6778c2ecf20Sopenharmony_ci S5K5BAF_GAIN_GREEN_DEF, 1, 6788c2ecf20Sopenharmony_ci ctrls->gain_blue->val, 1, 6798c2ecf20Sopenharmony_ci 1); 6808c2ecf20Sopenharmony_ci 6818c2ecf20Sopenharmony_ci s5k5baf_hw_set_alg(state, AALG_WB_EN, awb); 6828c2ecf20Sopenharmony_ci} 6838c2ecf20Sopenharmony_ci 6848c2ecf20Sopenharmony_ci/* Program FW with exposure time, 'exposure' in us units */ 6858c2ecf20Sopenharmony_cistatic void s5k5baf_hw_set_user_exposure(struct s5k5baf *state, int exposure) 6868c2ecf20Sopenharmony_ci{ 6878c2ecf20Sopenharmony_ci unsigned int time = exposure / 10; 6888c2ecf20Sopenharmony_ci 6898c2ecf20Sopenharmony_ci s5k5baf_write_seq(state, REG_SF_USR_EXPOSURE_L, 6908c2ecf20Sopenharmony_ci time & 0xffff, time >> 16, 1); 6918c2ecf20Sopenharmony_ci} 6928c2ecf20Sopenharmony_ci 6938c2ecf20Sopenharmony_cistatic void s5k5baf_hw_set_user_gain(struct s5k5baf *state, int gain) 6948c2ecf20Sopenharmony_ci{ 6958c2ecf20Sopenharmony_ci s5k5baf_write_seq(state, REG_SF_USR_TOT_GAIN, gain, 1); 6968c2ecf20Sopenharmony_ci} 6978c2ecf20Sopenharmony_ci 6988c2ecf20Sopenharmony_ci/* Set auto/manual exposure and total gain */ 6998c2ecf20Sopenharmony_cistatic void s5k5baf_hw_set_auto_exposure(struct s5k5baf *state, int value) 7008c2ecf20Sopenharmony_ci{ 7018c2ecf20Sopenharmony_ci if (value == V4L2_EXPOSURE_AUTO) { 7028c2ecf20Sopenharmony_ci s5k5baf_hw_set_alg(state, AALG_AE_EN | AALG_DIVLEI_EN, true); 7038c2ecf20Sopenharmony_ci } else { 7048c2ecf20Sopenharmony_ci unsigned int exp_time = state->ctrls.exposure->val; 7058c2ecf20Sopenharmony_ci 7068c2ecf20Sopenharmony_ci s5k5baf_hw_set_user_exposure(state, exp_time); 7078c2ecf20Sopenharmony_ci s5k5baf_hw_set_user_gain(state, state->ctrls.gain->val); 7088c2ecf20Sopenharmony_ci s5k5baf_hw_set_alg(state, AALG_AE_EN | AALG_DIVLEI_EN, false); 7098c2ecf20Sopenharmony_ci } 7108c2ecf20Sopenharmony_ci} 7118c2ecf20Sopenharmony_ci 7128c2ecf20Sopenharmony_cistatic void s5k5baf_hw_set_anti_flicker(struct s5k5baf *state, int v) 7138c2ecf20Sopenharmony_ci{ 7148c2ecf20Sopenharmony_ci if (v == V4L2_CID_POWER_LINE_FREQUENCY_AUTO) { 7158c2ecf20Sopenharmony_ci s5k5baf_hw_set_alg(state, AALG_FLICKER_EN, true); 7168c2ecf20Sopenharmony_ci } else { 7178c2ecf20Sopenharmony_ci /* The V4L2_CID_LINE_FREQUENCY control values match 7188c2ecf20Sopenharmony_ci * the register values */ 7198c2ecf20Sopenharmony_ci s5k5baf_write_seq(state, REG_SF_FLICKER_QUANT, v, 1); 7208c2ecf20Sopenharmony_ci s5k5baf_hw_set_alg(state, AALG_FLICKER_EN, false); 7218c2ecf20Sopenharmony_ci } 7228c2ecf20Sopenharmony_ci} 7238c2ecf20Sopenharmony_ci 7248c2ecf20Sopenharmony_cistatic void s5k5baf_hw_set_colorfx(struct s5k5baf *state, int val) 7258c2ecf20Sopenharmony_ci{ 7268c2ecf20Sopenharmony_ci static const u16 colorfx[] = { 7278c2ecf20Sopenharmony_ci [V4L2_COLORFX_NONE] = 0, 7288c2ecf20Sopenharmony_ci [V4L2_COLORFX_BW] = 1, 7298c2ecf20Sopenharmony_ci [V4L2_COLORFX_NEGATIVE] = 2, 7308c2ecf20Sopenharmony_ci [V4L2_COLORFX_SEPIA] = 3, 7318c2ecf20Sopenharmony_ci [V4L2_COLORFX_SKY_BLUE] = 4, 7328c2ecf20Sopenharmony_ci [V4L2_COLORFX_SKETCH] = 5, 7338c2ecf20Sopenharmony_ci }; 7348c2ecf20Sopenharmony_ci 7358c2ecf20Sopenharmony_ci s5k5baf_write(state, REG_G_SPEC_EFFECTS, colorfx[val]); 7368c2ecf20Sopenharmony_ci} 7378c2ecf20Sopenharmony_ci 7388c2ecf20Sopenharmony_cistatic int s5k5baf_find_pixfmt(struct v4l2_mbus_framefmt *mf) 7398c2ecf20Sopenharmony_ci{ 7408c2ecf20Sopenharmony_ci int i, c = -1; 7418c2ecf20Sopenharmony_ci 7428c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(s5k5baf_formats); i++) { 7438c2ecf20Sopenharmony_ci if (mf->colorspace != s5k5baf_formats[i].colorspace) 7448c2ecf20Sopenharmony_ci continue; 7458c2ecf20Sopenharmony_ci if (mf->code == s5k5baf_formats[i].code) 7468c2ecf20Sopenharmony_ci return i; 7478c2ecf20Sopenharmony_ci if (c < 0) 7488c2ecf20Sopenharmony_ci c = i; 7498c2ecf20Sopenharmony_ci } 7508c2ecf20Sopenharmony_ci return (c < 0) ? 0 : c; 7518c2ecf20Sopenharmony_ci} 7528c2ecf20Sopenharmony_ci 7538c2ecf20Sopenharmony_cistatic int s5k5baf_clear_error(struct s5k5baf *state) 7548c2ecf20Sopenharmony_ci{ 7558c2ecf20Sopenharmony_ci int ret = state->error; 7568c2ecf20Sopenharmony_ci 7578c2ecf20Sopenharmony_ci state->error = 0; 7588c2ecf20Sopenharmony_ci return ret; 7598c2ecf20Sopenharmony_ci} 7608c2ecf20Sopenharmony_ci 7618c2ecf20Sopenharmony_cistatic int s5k5baf_hw_set_video_bus(struct s5k5baf *state) 7628c2ecf20Sopenharmony_ci{ 7638c2ecf20Sopenharmony_ci u16 en_pkts; 7648c2ecf20Sopenharmony_ci 7658c2ecf20Sopenharmony_ci if (state->bus_type == V4L2_MBUS_CSI2_DPHY) 7668c2ecf20Sopenharmony_ci en_pkts = EN_PACKETS_CSI2; 7678c2ecf20Sopenharmony_ci else 7688c2ecf20Sopenharmony_ci en_pkts = 0; 7698c2ecf20Sopenharmony_ci 7708c2ecf20Sopenharmony_ci s5k5baf_write_seq(state, REG_OIF_EN_MIPI_LANES, 7718c2ecf20Sopenharmony_ci state->nlanes, en_pkts, 1); 7728c2ecf20Sopenharmony_ci 7738c2ecf20Sopenharmony_ci return s5k5baf_clear_error(state); 7748c2ecf20Sopenharmony_ci} 7758c2ecf20Sopenharmony_ci 7768c2ecf20Sopenharmony_cistatic u16 s5k5baf_get_cfg_error(struct s5k5baf *state) 7778c2ecf20Sopenharmony_ci{ 7788c2ecf20Sopenharmony_ci u16 err = s5k5baf_read(state, REG_G_PREV_CFG_ERROR); 7798c2ecf20Sopenharmony_ci if (err) 7808c2ecf20Sopenharmony_ci s5k5baf_write(state, REG_G_PREV_CFG_ERROR, 0); 7818c2ecf20Sopenharmony_ci return err; 7828c2ecf20Sopenharmony_ci} 7838c2ecf20Sopenharmony_ci 7848c2ecf20Sopenharmony_cistatic void s5k5baf_hw_set_fiv(struct s5k5baf *state, u16 fiv) 7858c2ecf20Sopenharmony_ci{ 7868c2ecf20Sopenharmony_ci s5k5baf_write(state, REG_P_MAX_FR_TIME(0), fiv); 7878c2ecf20Sopenharmony_ci s5k5baf_hw_sync_cfg(state); 7888c2ecf20Sopenharmony_ci} 7898c2ecf20Sopenharmony_ci 7908c2ecf20Sopenharmony_cistatic void s5k5baf_hw_find_min_fiv(struct s5k5baf *state) 7918c2ecf20Sopenharmony_ci{ 7928c2ecf20Sopenharmony_ci u16 err, fiv; 7938c2ecf20Sopenharmony_ci int n; 7948c2ecf20Sopenharmony_ci 7958c2ecf20Sopenharmony_ci fiv = s5k5baf_read(state, REG_G_ACTUAL_P_FR_TIME); 7968c2ecf20Sopenharmony_ci if (state->error) 7978c2ecf20Sopenharmony_ci return; 7988c2ecf20Sopenharmony_ci 7998c2ecf20Sopenharmony_ci for (n = 5; n > 0; --n) { 8008c2ecf20Sopenharmony_ci s5k5baf_hw_set_fiv(state, fiv); 8018c2ecf20Sopenharmony_ci err = s5k5baf_get_cfg_error(state); 8028c2ecf20Sopenharmony_ci if (state->error) 8038c2ecf20Sopenharmony_ci return; 8048c2ecf20Sopenharmony_ci switch (err) { 8058c2ecf20Sopenharmony_ci case CFG_ERROR_RANGE: 8068c2ecf20Sopenharmony_ci ++fiv; 8078c2ecf20Sopenharmony_ci break; 8088c2ecf20Sopenharmony_ci case 0: 8098c2ecf20Sopenharmony_ci state->fiv = fiv; 8108c2ecf20Sopenharmony_ci v4l2_info(&state->sd, 8118c2ecf20Sopenharmony_ci "found valid frame interval: %d00us\n", fiv); 8128c2ecf20Sopenharmony_ci return; 8138c2ecf20Sopenharmony_ci default: 8148c2ecf20Sopenharmony_ci v4l2_err(&state->sd, 8158c2ecf20Sopenharmony_ci "error setting frame interval: %d\n", err); 8168c2ecf20Sopenharmony_ci state->error = -EINVAL; 8178c2ecf20Sopenharmony_ci } 8188c2ecf20Sopenharmony_ci } 8198c2ecf20Sopenharmony_ci v4l2_err(&state->sd, "cannot find correct frame interval\n"); 8208c2ecf20Sopenharmony_ci state->error = -ERANGE; 8218c2ecf20Sopenharmony_ci} 8228c2ecf20Sopenharmony_ci 8238c2ecf20Sopenharmony_cistatic void s5k5baf_hw_validate_cfg(struct s5k5baf *state) 8248c2ecf20Sopenharmony_ci{ 8258c2ecf20Sopenharmony_ci u16 err; 8268c2ecf20Sopenharmony_ci 8278c2ecf20Sopenharmony_ci err = s5k5baf_get_cfg_error(state); 8288c2ecf20Sopenharmony_ci if (state->error) 8298c2ecf20Sopenharmony_ci return; 8308c2ecf20Sopenharmony_ci 8318c2ecf20Sopenharmony_ci switch (err) { 8328c2ecf20Sopenharmony_ci case 0: 8338c2ecf20Sopenharmony_ci state->apply_cfg = 1; 8348c2ecf20Sopenharmony_ci return; 8358c2ecf20Sopenharmony_ci case CFG_ERROR_RANGE: 8368c2ecf20Sopenharmony_ci s5k5baf_hw_find_min_fiv(state); 8378c2ecf20Sopenharmony_ci if (!state->error) 8388c2ecf20Sopenharmony_ci state->apply_cfg = 1; 8398c2ecf20Sopenharmony_ci return; 8408c2ecf20Sopenharmony_ci default: 8418c2ecf20Sopenharmony_ci v4l2_err(&state->sd, 8428c2ecf20Sopenharmony_ci "error setting format: %d\n", err); 8438c2ecf20Sopenharmony_ci state->error = -EINVAL; 8448c2ecf20Sopenharmony_ci } 8458c2ecf20Sopenharmony_ci} 8468c2ecf20Sopenharmony_ci 8478c2ecf20Sopenharmony_cistatic void s5k5baf_rescale(struct v4l2_rect *r, const struct v4l2_rect *v, 8488c2ecf20Sopenharmony_ci const struct v4l2_rect *n, 8498c2ecf20Sopenharmony_ci const struct v4l2_rect *d) 8508c2ecf20Sopenharmony_ci{ 8518c2ecf20Sopenharmony_ci r->left = v->left * n->width / d->width; 8528c2ecf20Sopenharmony_ci r->top = v->top * n->height / d->height; 8538c2ecf20Sopenharmony_ci r->width = v->width * n->width / d->width; 8548c2ecf20Sopenharmony_ci r->height = v->height * n->height / d->height; 8558c2ecf20Sopenharmony_ci} 8568c2ecf20Sopenharmony_ci 8578c2ecf20Sopenharmony_cistatic int s5k5baf_hw_set_crop_rects(struct s5k5baf *state) 8588c2ecf20Sopenharmony_ci{ 8598c2ecf20Sopenharmony_ci struct v4l2_rect *p, r; 8608c2ecf20Sopenharmony_ci u16 err; 8618c2ecf20Sopenharmony_ci int ret; 8628c2ecf20Sopenharmony_ci 8638c2ecf20Sopenharmony_ci p = &state->crop_sink; 8648c2ecf20Sopenharmony_ci s5k5baf_write_seq(state, REG_G_PREVREQ_IN_WIDTH, p->width, p->height, 8658c2ecf20Sopenharmony_ci p->left, p->top); 8668c2ecf20Sopenharmony_ci 8678c2ecf20Sopenharmony_ci s5k5baf_rescale(&r, &state->crop_source, &state->crop_sink, 8688c2ecf20Sopenharmony_ci &state->compose); 8698c2ecf20Sopenharmony_ci s5k5baf_write_seq(state, REG_G_PREVZOOM_IN_WIDTH, r.width, r.height, 8708c2ecf20Sopenharmony_ci r.left, r.top); 8718c2ecf20Sopenharmony_ci 8728c2ecf20Sopenharmony_ci s5k5baf_synchronize(state, 500, REG_G_INPUTS_CHANGE_REQ); 8738c2ecf20Sopenharmony_ci s5k5baf_synchronize(state, 500, REG_G_PREV_CFG_BYPASS_CHANGED); 8748c2ecf20Sopenharmony_ci err = s5k5baf_get_cfg_error(state); 8758c2ecf20Sopenharmony_ci ret = s5k5baf_clear_error(state); 8768c2ecf20Sopenharmony_ci if (ret < 0) 8778c2ecf20Sopenharmony_ci return ret; 8788c2ecf20Sopenharmony_ci 8798c2ecf20Sopenharmony_ci switch (err) { 8808c2ecf20Sopenharmony_ci case 0: 8818c2ecf20Sopenharmony_ci break; 8828c2ecf20Sopenharmony_ci case CFG_ERROR_RANGE: 8838c2ecf20Sopenharmony_ci /* retry crop with frame interval set to max */ 8848c2ecf20Sopenharmony_ci s5k5baf_hw_set_fiv(state, S5K5BAF_MAX_FR_TIME); 8858c2ecf20Sopenharmony_ci err = s5k5baf_get_cfg_error(state); 8868c2ecf20Sopenharmony_ci ret = s5k5baf_clear_error(state); 8878c2ecf20Sopenharmony_ci if (ret < 0) 8888c2ecf20Sopenharmony_ci return ret; 8898c2ecf20Sopenharmony_ci if (err) { 8908c2ecf20Sopenharmony_ci v4l2_err(&state->sd, 8918c2ecf20Sopenharmony_ci "crop error on max frame interval: %d\n", err); 8928c2ecf20Sopenharmony_ci state->error = -EINVAL; 8938c2ecf20Sopenharmony_ci } 8948c2ecf20Sopenharmony_ci s5k5baf_hw_set_fiv(state, state->req_fiv); 8958c2ecf20Sopenharmony_ci s5k5baf_hw_validate_cfg(state); 8968c2ecf20Sopenharmony_ci break; 8978c2ecf20Sopenharmony_ci default: 8988c2ecf20Sopenharmony_ci v4l2_err(&state->sd, "crop error: %d\n", err); 8998c2ecf20Sopenharmony_ci return -EINVAL; 9008c2ecf20Sopenharmony_ci } 9018c2ecf20Sopenharmony_ci 9028c2ecf20Sopenharmony_ci if (!state->apply_cfg) 9038c2ecf20Sopenharmony_ci return 0; 9048c2ecf20Sopenharmony_ci 9058c2ecf20Sopenharmony_ci p = &state->crop_source; 9068c2ecf20Sopenharmony_ci s5k5baf_write_seq(state, REG_P_OUT_WIDTH(0), p->width, p->height); 9078c2ecf20Sopenharmony_ci s5k5baf_hw_set_fiv(state, state->req_fiv); 9088c2ecf20Sopenharmony_ci s5k5baf_hw_validate_cfg(state); 9098c2ecf20Sopenharmony_ci 9108c2ecf20Sopenharmony_ci return s5k5baf_clear_error(state); 9118c2ecf20Sopenharmony_ci} 9128c2ecf20Sopenharmony_ci 9138c2ecf20Sopenharmony_cistatic void s5k5baf_hw_set_config(struct s5k5baf *state) 9148c2ecf20Sopenharmony_ci{ 9158c2ecf20Sopenharmony_ci u16 reg_fmt = s5k5baf_formats[state->pixfmt].reg_p_fmt; 9168c2ecf20Sopenharmony_ci struct v4l2_rect *r = &state->crop_source; 9178c2ecf20Sopenharmony_ci 9188c2ecf20Sopenharmony_ci s5k5baf_write_seq(state, REG_P_OUT_WIDTH(0), 9198c2ecf20Sopenharmony_ci r->width, r->height, reg_fmt, 9208c2ecf20Sopenharmony_ci PCLK_MAX_FREQ >> 2, PCLK_MIN_FREQ >> 2, 9218c2ecf20Sopenharmony_ci PVI_MASK_MIPI, CLK_MIPI_INDEX, 9228c2ecf20Sopenharmony_ci FR_RATE_FIXED, FR_RATE_Q_DYNAMIC, 9238c2ecf20Sopenharmony_ci state->req_fiv, S5K5BAF_MIN_FR_TIME); 9248c2ecf20Sopenharmony_ci s5k5baf_hw_sync_cfg(state); 9258c2ecf20Sopenharmony_ci s5k5baf_hw_validate_cfg(state); 9268c2ecf20Sopenharmony_ci} 9278c2ecf20Sopenharmony_ci 9288c2ecf20Sopenharmony_ci 9298c2ecf20Sopenharmony_cistatic void s5k5baf_hw_set_test_pattern(struct s5k5baf *state, int id) 9308c2ecf20Sopenharmony_ci{ 9318c2ecf20Sopenharmony_ci s5k5baf_i2c_write(state, REG_PATTERN_WIDTH, 800); 9328c2ecf20Sopenharmony_ci s5k5baf_i2c_write(state, REG_PATTERN_HEIGHT, 511); 9338c2ecf20Sopenharmony_ci s5k5baf_i2c_write(state, REG_PATTERN_PARAM, 0); 9348c2ecf20Sopenharmony_ci s5k5baf_i2c_write(state, REG_PATTERN_SET, id); 9358c2ecf20Sopenharmony_ci} 9368c2ecf20Sopenharmony_ci 9378c2ecf20Sopenharmony_cistatic void s5k5baf_gpio_assert(struct s5k5baf *state, int id) 9388c2ecf20Sopenharmony_ci{ 9398c2ecf20Sopenharmony_ci struct s5k5baf_gpio *gpio = &state->gpios[id]; 9408c2ecf20Sopenharmony_ci 9418c2ecf20Sopenharmony_ci gpio_set_value(gpio->gpio, gpio->level); 9428c2ecf20Sopenharmony_ci} 9438c2ecf20Sopenharmony_ci 9448c2ecf20Sopenharmony_cistatic void s5k5baf_gpio_deassert(struct s5k5baf *state, int id) 9458c2ecf20Sopenharmony_ci{ 9468c2ecf20Sopenharmony_ci struct s5k5baf_gpio *gpio = &state->gpios[id]; 9478c2ecf20Sopenharmony_ci 9488c2ecf20Sopenharmony_ci gpio_set_value(gpio->gpio, !gpio->level); 9498c2ecf20Sopenharmony_ci} 9508c2ecf20Sopenharmony_ci 9518c2ecf20Sopenharmony_cistatic int s5k5baf_power_on(struct s5k5baf *state) 9528c2ecf20Sopenharmony_ci{ 9538c2ecf20Sopenharmony_ci int ret; 9548c2ecf20Sopenharmony_ci 9558c2ecf20Sopenharmony_ci ret = regulator_bulk_enable(S5K5BAF_NUM_SUPPLIES, state->supplies); 9568c2ecf20Sopenharmony_ci if (ret < 0) 9578c2ecf20Sopenharmony_ci goto err; 9588c2ecf20Sopenharmony_ci 9598c2ecf20Sopenharmony_ci ret = clk_set_rate(state->clock, state->mclk_frequency); 9608c2ecf20Sopenharmony_ci if (ret < 0) 9618c2ecf20Sopenharmony_ci goto err_reg_dis; 9628c2ecf20Sopenharmony_ci 9638c2ecf20Sopenharmony_ci ret = clk_prepare_enable(state->clock); 9648c2ecf20Sopenharmony_ci if (ret < 0) 9658c2ecf20Sopenharmony_ci goto err_reg_dis; 9668c2ecf20Sopenharmony_ci 9678c2ecf20Sopenharmony_ci v4l2_dbg(1, debug, &state->sd, "clock frequency: %ld\n", 9688c2ecf20Sopenharmony_ci clk_get_rate(state->clock)); 9698c2ecf20Sopenharmony_ci 9708c2ecf20Sopenharmony_ci s5k5baf_gpio_deassert(state, STBY); 9718c2ecf20Sopenharmony_ci usleep_range(50, 100); 9728c2ecf20Sopenharmony_ci s5k5baf_gpio_deassert(state, RSET); 9738c2ecf20Sopenharmony_ci return 0; 9748c2ecf20Sopenharmony_ci 9758c2ecf20Sopenharmony_cierr_reg_dis: 9768c2ecf20Sopenharmony_ci regulator_bulk_disable(S5K5BAF_NUM_SUPPLIES, state->supplies); 9778c2ecf20Sopenharmony_cierr: 9788c2ecf20Sopenharmony_ci v4l2_err(&state->sd, "%s() failed (%d)\n", __func__, ret); 9798c2ecf20Sopenharmony_ci return ret; 9808c2ecf20Sopenharmony_ci} 9818c2ecf20Sopenharmony_ci 9828c2ecf20Sopenharmony_cistatic int s5k5baf_power_off(struct s5k5baf *state) 9838c2ecf20Sopenharmony_ci{ 9848c2ecf20Sopenharmony_ci int ret; 9858c2ecf20Sopenharmony_ci 9868c2ecf20Sopenharmony_ci state->streaming = 0; 9878c2ecf20Sopenharmony_ci state->apply_cfg = 0; 9888c2ecf20Sopenharmony_ci state->apply_crop = 0; 9898c2ecf20Sopenharmony_ci 9908c2ecf20Sopenharmony_ci s5k5baf_gpio_assert(state, RSET); 9918c2ecf20Sopenharmony_ci s5k5baf_gpio_assert(state, STBY); 9928c2ecf20Sopenharmony_ci 9938c2ecf20Sopenharmony_ci if (!IS_ERR(state->clock)) 9948c2ecf20Sopenharmony_ci clk_disable_unprepare(state->clock); 9958c2ecf20Sopenharmony_ci 9968c2ecf20Sopenharmony_ci ret = regulator_bulk_disable(S5K5BAF_NUM_SUPPLIES, 9978c2ecf20Sopenharmony_ci state->supplies); 9988c2ecf20Sopenharmony_ci if (ret < 0) 9998c2ecf20Sopenharmony_ci v4l2_err(&state->sd, "failed to disable regulators\n"); 10008c2ecf20Sopenharmony_ci 10018c2ecf20Sopenharmony_ci return 0; 10028c2ecf20Sopenharmony_ci} 10038c2ecf20Sopenharmony_ci 10048c2ecf20Sopenharmony_cistatic void s5k5baf_hw_init(struct s5k5baf *state) 10058c2ecf20Sopenharmony_ci{ 10068c2ecf20Sopenharmony_ci s5k5baf_i2c_write(state, AHB_MSB_ADDR_PTR, PAGE_IF_HW); 10078c2ecf20Sopenharmony_ci s5k5baf_i2c_write(state, REG_CLEAR_HOST_INT, 0); 10088c2ecf20Sopenharmony_ci s5k5baf_i2c_write(state, REG_SW_LOAD_COMPLETE, 1); 10098c2ecf20Sopenharmony_ci s5k5baf_i2c_write(state, REG_CMDRD_PAGE, PAGE_IF_SW); 10108c2ecf20Sopenharmony_ci s5k5baf_i2c_write(state, REG_CMDWR_PAGE, PAGE_IF_SW); 10118c2ecf20Sopenharmony_ci} 10128c2ecf20Sopenharmony_ci 10138c2ecf20Sopenharmony_ci/* 10148c2ecf20Sopenharmony_ci * V4L2 subdev core and video operations 10158c2ecf20Sopenharmony_ci */ 10168c2ecf20Sopenharmony_ci 10178c2ecf20Sopenharmony_cistatic void s5k5baf_initialize_data(struct s5k5baf *state) 10188c2ecf20Sopenharmony_ci{ 10198c2ecf20Sopenharmony_ci state->pixfmt = 0; 10208c2ecf20Sopenharmony_ci state->req_fiv = 10000 / 15; 10218c2ecf20Sopenharmony_ci state->fiv = state->req_fiv; 10228c2ecf20Sopenharmony_ci state->valid_auto_alg = 0; 10238c2ecf20Sopenharmony_ci} 10248c2ecf20Sopenharmony_ci 10258c2ecf20Sopenharmony_cistatic int s5k5baf_load_setfile(struct s5k5baf *state) 10268c2ecf20Sopenharmony_ci{ 10278c2ecf20Sopenharmony_ci struct i2c_client *c = v4l2_get_subdevdata(&state->sd); 10288c2ecf20Sopenharmony_ci const struct firmware *fw; 10298c2ecf20Sopenharmony_ci int ret; 10308c2ecf20Sopenharmony_ci 10318c2ecf20Sopenharmony_ci ret = request_firmware(&fw, S5K5BAF_FW_FILENAME, &c->dev); 10328c2ecf20Sopenharmony_ci if (ret < 0) { 10338c2ecf20Sopenharmony_ci dev_warn(&c->dev, "firmware file (%s) not loaded\n", 10348c2ecf20Sopenharmony_ci S5K5BAF_FW_FILENAME); 10358c2ecf20Sopenharmony_ci return ret; 10368c2ecf20Sopenharmony_ci } 10378c2ecf20Sopenharmony_ci 10388c2ecf20Sopenharmony_ci ret = s5k5baf_fw_parse(&c->dev, &state->fw, fw->size / 2, 10398c2ecf20Sopenharmony_ci (__le16 *)fw->data); 10408c2ecf20Sopenharmony_ci 10418c2ecf20Sopenharmony_ci release_firmware(fw); 10428c2ecf20Sopenharmony_ci 10438c2ecf20Sopenharmony_ci return ret; 10448c2ecf20Sopenharmony_ci} 10458c2ecf20Sopenharmony_ci 10468c2ecf20Sopenharmony_cistatic int s5k5baf_set_power(struct v4l2_subdev *sd, int on) 10478c2ecf20Sopenharmony_ci{ 10488c2ecf20Sopenharmony_ci struct s5k5baf *state = to_s5k5baf(sd); 10498c2ecf20Sopenharmony_ci int ret = 0; 10508c2ecf20Sopenharmony_ci 10518c2ecf20Sopenharmony_ci mutex_lock(&state->lock); 10528c2ecf20Sopenharmony_ci 10538c2ecf20Sopenharmony_ci if (state->power != !on) 10548c2ecf20Sopenharmony_ci goto out; 10558c2ecf20Sopenharmony_ci 10568c2ecf20Sopenharmony_ci if (on) { 10578c2ecf20Sopenharmony_ci if (state->fw == NULL) 10588c2ecf20Sopenharmony_ci s5k5baf_load_setfile(state); 10598c2ecf20Sopenharmony_ci 10608c2ecf20Sopenharmony_ci s5k5baf_initialize_data(state); 10618c2ecf20Sopenharmony_ci ret = s5k5baf_power_on(state); 10628c2ecf20Sopenharmony_ci if (ret < 0) 10638c2ecf20Sopenharmony_ci goto out; 10648c2ecf20Sopenharmony_ci 10658c2ecf20Sopenharmony_ci s5k5baf_hw_init(state); 10668c2ecf20Sopenharmony_ci s5k5baf_hw_patch(state); 10678c2ecf20Sopenharmony_ci s5k5baf_i2c_write(state, REG_SET_HOST_INT, 1); 10688c2ecf20Sopenharmony_ci s5k5baf_hw_set_clocks(state); 10698c2ecf20Sopenharmony_ci 10708c2ecf20Sopenharmony_ci ret = s5k5baf_hw_set_video_bus(state); 10718c2ecf20Sopenharmony_ci if (ret < 0) 10728c2ecf20Sopenharmony_ci goto out; 10738c2ecf20Sopenharmony_ci 10748c2ecf20Sopenharmony_ci s5k5baf_hw_set_cis(state); 10758c2ecf20Sopenharmony_ci s5k5baf_hw_set_ccm(state); 10768c2ecf20Sopenharmony_ci 10778c2ecf20Sopenharmony_ci ret = s5k5baf_clear_error(state); 10788c2ecf20Sopenharmony_ci if (!ret) 10798c2ecf20Sopenharmony_ci state->power++; 10808c2ecf20Sopenharmony_ci } else { 10818c2ecf20Sopenharmony_ci s5k5baf_power_off(state); 10828c2ecf20Sopenharmony_ci state->power--; 10838c2ecf20Sopenharmony_ci } 10848c2ecf20Sopenharmony_ci 10858c2ecf20Sopenharmony_ciout: 10868c2ecf20Sopenharmony_ci mutex_unlock(&state->lock); 10878c2ecf20Sopenharmony_ci 10888c2ecf20Sopenharmony_ci if (!ret && on) 10898c2ecf20Sopenharmony_ci ret = v4l2_ctrl_handler_setup(&state->ctrls.handler); 10908c2ecf20Sopenharmony_ci 10918c2ecf20Sopenharmony_ci return ret; 10928c2ecf20Sopenharmony_ci} 10938c2ecf20Sopenharmony_ci 10948c2ecf20Sopenharmony_cistatic void s5k5baf_hw_set_stream(struct s5k5baf *state, int enable) 10958c2ecf20Sopenharmony_ci{ 10968c2ecf20Sopenharmony_ci s5k5baf_write_seq(state, REG_G_ENABLE_PREV, enable, 1); 10978c2ecf20Sopenharmony_ci} 10988c2ecf20Sopenharmony_ci 10998c2ecf20Sopenharmony_cistatic int s5k5baf_s_stream(struct v4l2_subdev *sd, int on) 11008c2ecf20Sopenharmony_ci{ 11018c2ecf20Sopenharmony_ci struct s5k5baf *state = to_s5k5baf(sd); 11028c2ecf20Sopenharmony_ci int ret; 11038c2ecf20Sopenharmony_ci 11048c2ecf20Sopenharmony_ci mutex_lock(&state->lock); 11058c2ecf20Sopenharmony_ci 11068c2ecf20Sopenharmony_ci if (state->streaming == !!on) { 11078c2ecf20Sopenharmony_ci ret = 0; 11088c2ecf20Sopenharmony_ci goto out; 11098c2ecf20Sopenharmony_ci } 11108c2ecf20Sopenharmony_ci 11118c2ecf20Sopenharmony_ci if (on) { 11128c2ecf20Sopenharmony_ci s5k5baf_hw_set_config(state); 11138c2ecf20Sopenharmony_ci ret = s5k5baf_hw_set_crop_rects(state); 11148c2ecf20Sopenharmony_ci if (ret < 0) 11158c2ecf20Sopenharmony_ci goto out; 11168c2ecf20Sopenharmony_ci s5k5baf_hw_set_stream(state, 1); 11178c2ecf20Sopenharmony_ci s5k5baf_i2c_write(state, 0xb0cc, 0x000b); 11188c2ecf20Sopenharmony_ci } else { 11198c2ecf20Sopenharmony_ci s5k5baf_hw_set_stream(state, 0); 11208c2ecf20Sopenharmony_ci } 11218c2ecf20Sopenharmony_ci ret = s5k5baf_clear_error(state); 11228c2ecf20Sopenharmony_ci if (!ret) 11238c2ecf20Sopenharmony_ci state->streaming = !state->streaming; 11248c2ecf20Sopenharmony_ci 11258c2ecf20Sopenharmony_ciout: 11268c2ecf20Sopenharmony_ci mutex_unlock(&state->lock); 11278c2ecf20Sopenharmony_ci 11288c2ecf20Sopenharmony_ci return ret; 11298c2ecf20Sopenharmony_ci} 11308c2ecf20Sopenharmony_ci 11318c2ecf20Sopenharmony_cistatic int s5k5baf_g_frame_interval(struct v4l2_subdev *sd, 11328c2ecf20Sopenharmony_ci struct v4l2_subdev_frame_interval *fi) 11338c2ecf20Sopenharmony_ci{ 11348c2ecf20Sopenharmony_ci struct s5k5baf *state = to_s5k5baf(sd); 11358c2ecf20Sopenharmony_ci 11368c2ecf20Sopenharmony_ci mutex_lock(&state->lock); 11378c2ecf20Sopenharmony_ci fi->interval.numerator = state->fiv; 11388c2ecf20Sopenharmony_ci fi->interval.denominator = 10000; 11398c2ecf20Sopenharmony_ci mutex_unlock(&state->lock); 11408c2ecf20Sopenharmony_ci 11418c2ecf20Sopenharmony_ci return 0; 11428c2ecf20Sopenharmony_ci} 11438c2ecf20Sopenharmony_ci 11448c2ecf20Sopenharmony_cistatic void s5k5baf_set_frame_interval(struct s5k5baf *state, 11458c2ecf20Sopenharmony_ci struct v4l2_subdev_frame_interval *fi) 11468c2ecf20Sopenharmony_ci{ 11478c2ecf20Sopenharmony_ci struct v4l2_fract *i = &fi->interval; 11488c2ecf20Sopenharmony_ci 11498c2ecf20Sopenharmony_ci if (fi->interval.denominator == 0) 11508c2ecf20Sopenharmony_ci state->req_fiv = S5K5BAF_MAX_FR_TIME; 11518c2ecf20Sopenharmony_ci else 11528c2ecf20Sopenharmony_ci state->req_fiv = clamp_t(u32, 11538c2ecf20Sopenharmony_ci i->numerator * 10000 / i->denominator, 11548c2ecf20Sopenharmony_ci S5K5BAF_MIN_FR_TIME, 11558c2ecf20Sopenharmony_ci S5K5BAF_MAX_FR_TIME); 11568c2ecf20Sopenharmony_ci 11578c2ecf20Sopenharmony_ci state->fiv = state->req_fiv; 11588c2ecf20Sopenharmony_ci if (state->apply_cfg) { 11598c2ecf20Sopenharmony_ci s5k5baf_hw_set_fiv(state, state->req_fiv); 11608c2ecf20Sopenharmony_ci s5k5baf_hw_validate_cfg(state); 11618c2ecf20Sopenharmony_ci } 11628c2ecf20Sopenharmony_ci *i = (struct v4l2_fract){ state->fiv, 10000 }; 11638c2ecf20Sopenharmony_ci if (state->fiv == state->req_fiv) 11648c2ecf20Sopenharmony_ci v4l2_info(&state->sd, "frame interval changed to %d00us\n", 11658c2ecf20Sopenharmony_ci state->fiv); 11668c2ecf20Sopenharmony_ci} 11678c2ecf20Sopenharmony_ci 11688c2ecf20Sopenharmony_cistatic int s5k5baf_s_frame_interval(struct v4l2_subdev *sd, 11698c2ecf20Sopenharmony_ci struct v4l2_subdev_frame_interval *fi) 11708c2ecf20Sopenharmony_ci{ 11718c2ecf20Sopenharmony_ci struct s5k5baf *state = to_s5k5baf(sd); 11728c2ecf20Sopenharmony_ci 11738c2ecf20Sopenharmony_ci mutex_lock(&state->lock); 11748c2ecf20Sopenharmony_ci s5k5baf_set_frame_interval(state, fi); 11758c2ecf20Sopenharmony_ci mutex_unlock(&state->lock); 11768c2ecf20Sopenharmony_ci return 0; 11778c2ecf20Sopenharmony_ci} 11788c2ecf20Sopenharmony_ci 11798c2ecf20Sopenharmony_ci/* 11808c2ecf20Sopenharmony_ci * V4L2 subdev pad level and video operations 11818c2ecf20Sopenharmony_ci */ 11828c2ecf20Sopenharmony_cistatic int s5k5baf_enum_frame_interval(struct v4l2_subdev *sd, 11838c2ecf20Sopenharmony_ci struct v4l2_subdev_pad_config *cfg, 11848c2ecf20Sopenharmony_ci struct v4l2_subdev_frame_interval_enum *fie) 11858c2ecf20Sopenharmony_ci{ 11868c2ecf20Sopenharmony_ci if (fie->index > S5K5BAF_MAX_FR_TIME - S5K5BAF_MIN_FR_TIME || 11878c2ecf20Sopenharmony_ci fie->pad != PAD_CIS) 11888c2ecf20Sopenharmony_ci return -EINVAL; 11898c2ecf20Sopenharmony_ci 11908c2ecf20Sopenharmony_ci v4l_bound_align_image(&fie->width, S5K5BAF_WIN_WIDTH_MIN, 11918c2ecf20Sopenharmony_ci S5K5BAF_CIS_WIDTH, 1, 11928c2ecf20Sopenharmony_ci &fie->height, S5K5BAF_WIN_HEIGHT_MIN, 11938c2ecf20Sopenharmony_ci S5K5BAF_CIS_HEIGHT, 1, 0); 11948c2ecf20Sopenharmony_ci 11958c2ecf20Sopenharmony_ci fie->interval.numerator = S5K5BAF_MIN_FR_TIME + fie->index; 11968c2ecf20Sopenharmony_ci fie->interval.denominator = 10000; 11978c2ecf20Sopenharmony_ci 11988c2ecf20Sopenharmony_ci return 0; 11998c2ecf20Sopenharmony_ci} 12008c2ecf20Sopenharmony_ci 12018c2ecf20Sopenharmony_cistatic int s5k5baf_enum_mbus_code(struct v4l2_subdev *sd, 12028c2ecf20Sopenharmony_ci struct v4l2_subdev_pad_config *cfg, 12038c2ecf20Sopenharmony_ci struct v4l2_subdev_mbus_code_enum *code) 12048c2ecf20Sopenharmony_ci{ 12058c2ecf20Sopenharmony_ci if (code->pad == PAD_CIS) { 12068c2ecf20Sopenharmony_ci if (code->index > 0) 12078c2ecf20Sopenharmony_ci return -EINVAL; 12088c2ecf20Sopenharmony_ci code->code = MEDIA_BUS_FMT_FIXED; 12098c2ecf20Sopenharmony_ci return 0; 12108c2ecf20Sopenharmony_ci } 12118c2ecf20Sopenharmony_ci 12128c2ecf20Sopenharmony_ci if (code->index >= ARRAY_SIZE(s5k5baf_formats)) 12138c2ecf20Sopenharmony_ci return -EINVAL; 12148c2ecf20Sopenharmony_ci 12158c2ecf20Sopenharmony_ci code->code = s5k5baf_formats[code->index].code; 12168c2ecf20Sopenharmony_ci return 0; 12178c2ecf20Sopenharmony_ci} 12188c2ecf20Sopenharmony_ci 12198c2ecf20Sopenharmony_cistatic int s5k5baf_enum_frame_size(struct v4l2_subdev *sd, 12208c2ecf20Sopenharmony_ci struct v4l2_subdev_pad_config *cfg, 12218c2ecf20Sopenharmony_ci struct v4l2_subdev_frame_size_enum *fse) 12228c2ecf20Sopenharmony_ci{ 12238c2ecf20Sopenharmony_ci int i; 12248c2ecf20Sopenharmony_ci 12258c2ecf20Sopenharmony_ci if (fse->index > 0) 12268c2ecf20Sopenharmony_ci return -EINVAL; 12278c2ecf20Sopenharmony_ci 12288c2ecf20Sopenharmony_ci if (fse->pad == PAD_CIS) { 12298c2ecf20Sopenharmony_ci fse->code = MEDIA_BUS_FMT_FIXED; 12308c2ecf20Sopenharmony_ci fse->min_width = S5K5BAF_CIS_WIDTH; 12318c2ecf20Sopenharmony_ci fse->max_width = S5K5BAF_CIS_WIDTH; 12328c2ecf20Sopenharmony_ci fse->min_height = S5K5BAF_CIS_HEIGHT; 12338c2ecf20Sopenharmony_ci fse->max_height = S5K5BAF_CIS_HEIGHT; 12348c2ecf20Sopenharmony_ci return 0; 12358c2ecf20Sopenharmony_ci } 12368c2ecf20Sopenharmony_ci 12378c2ecf20Sopenharmony_ci i = ARRAY_SIZE(s5k5baf_formats); 12388c2ecf20Sopenharmony_ci while (--i) 12398c2ecf20Sopenharmony_ci if (fse->code == s5k5baf_formats[i].code) 12408c2ecf20Sopenharmony_ci break; 12418c2ecf20Sopenharmony_ci fse->code = s5k5baf_formats[i].code; 12428c2ecf20Sopenharmony_ci fse->min_width = S5K5BAF_WIN_WIDTH_MIN; 12438c2ecf20Sopenharmony_ci fse->max_width = S5K5BAF_CIS_WIDTH; 12448c2ecf20Sopenharmony_ci fse->max_height = S5K5BAF_WIN_HEIGHT_MIN; 12458c2ecf20Sopenharmony_ci fse->min_height = S5K5BAF_CIS_HEIGHT; 12468c2ecf20Sopenharmony_ci 12478c2ecf20Sopenharmony_ci return 0; 12488c2ecf20Sopenharmony_ci} 12498c2ecf20Sopenharmony_ci 12508c2ecf20Sopenharmony_cistatic void s5k5baf_try_cis_format(struct v4l2_mbus_framefmt *mf) 12518c2ecf20Sopenharmony_ci{ 12528c2ecf20Sopenharmony_ci mf->width = S5K5BAF_CIS_WIDTH; 12538c2ecf20Sopenharmony_ci mf->height = S5K5BAF_CIS_HEIGHT; 12548c2ecf20Sopenharmony_ci mf->code = MEDIA_BUS_FMT_FIXED; 12558c2ecf20Sopenharmony_ci mf->colorspace = V4L2_COLORSPACE_JPEG; 12568c2ecf20Sopenharmony_ci mf->field = V4L2_FIELD_NONE; 12578c2ecf20Sopenharmony_ci} 12588c2ecf20Sopenharmony_ci 12598c2ecf20Sopenharmony_cistatic int s5k5baf_try_isp_format(struct v4l2_mbus_framefmt *mf) 12608c2ecf20Sopenharmony_ci{ 12618c2ecf20Sopenharmony_ci int pixfmt; 12628c2ecf20Sopenharmony_ci 12638c2ecf20Sopenharmony_ci v4l_bound_align_image(&mf->width, S5K5BAF_WIN_WIDTH_MIN, 12648c2ecf20Sopenharmony_ci S5K5BAF_CIS_WIDTH, 1, 12658c2ecf20Sopenharmony_ci &mf->height, S5K5BAF_WIN_HEIGHT_MIN, 12668c2ecf20Sopenharmony_ci S5K5BAF_CIS_HEIGHT, 1, 0); 12678c2ecf20Sopenharmony_ci 12688c2ecf20Sopenharmony_ci pixfmt = s5k5baf_find_pixfmt(mf); 12698c2ecf20Sopenharmony_ci 12708c2ecf20Sopenharmony_ci mf->colorspace = s5k5baf_formats[pixfmt].colorspace; 12718c2ecf20Sopenharmony_ci mf->code = s5k5baf_formats[pixfmt].code; 12728c2ecf20Sopenharmony_ci mf->field = V4L2_FIELD_NONE; 12738c2ecf20Sopenharmony_ci 12748c2ecf20Sopenharmony_ci return pixfmt; 12758c2ecf20Sopenharmony_ci} 12768c2ecf20Sopenharmony_ci 12778c2ecf20Sopenharmony_cistatic int s5k5baf_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, 12788c2ecf20Sopenharmony_ci struct v4l2_subdev_format *fmt) 12798c2ecf20Sopenharmony_ci{ 12808c2ecf20Sopenharmony_ci struct s5k5baf *state = to_s5k5baf(sd); 12818c2ecf20Sopenharmony_ci const struct s5k5baf_pixfmt *pixfmt; 12828c2ecf20Sopenharmony_ci struct v4l2_mbus_framefmt *mf; 12838c2ecf20Sopenharmony_ci 12848c2ecf20Sopenharmony_ci if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { 12858c2ecf20Sopenharmony_ci mf = v4l2_subdev_get_try_format(sd, cfg, fmt->pad); 12868c2ecf20Sopenharmony_ci fmt->format = *mf; 12878c2ecf20Sopenharmony_ci return 0; 12888c2ecf20Sopenharmony_ci } 12898c2ecf20Sopenharmony_ci 12908c2ecf20Sopenharmony_ci mf = &fmt->format; 12918c2ecf20Sopenharmony_ci if (fmt->pad == PAD_CIS) { 12928c2ecf20Sopenharmony_ci s5k5baf_try_cis_format(mf); 12938c2ecf20Sopenharmony_ci return 0; 12948c2ecf20Sopenharmony_ci } 12958c2ecf20Sopenharmony_ci mf->field = V4L2_FIELD_NONE; 12968c2ecf20Sopenharmony_ci mutex_lock(&state->lock); 12978c2ecf20Sopenharmony_ci pixfmt = &s5k5baf_formats[state->pixfmt]; 12988c2ecf20Sopenharmony_ci mf->width = state->crop_source.width; 12998c2ecf20Sopenharmony_ci mf->height = state->crop_source.height; 13008c2ecf20Sopenharmony_ci mf->code = pixfmt->code; 13018c2ecf20Sopenharmony_ci mf->colorspace = pixfmt->colorspace; 13028c2ecf20Sopenharmony_ci mutex_unlock(&state->lock); 13038c2ecf20Sopenharmony_ci 13048c2ecf20Sopenharmony_ci return 0; 13058c2ecf20Sopenharmony_ci} 13068c2ecf20Sopenharmony_ci 13078c2ecf20Sopenharmony_cistatic int s5k5baf_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, 13088c2ecf20Sopenharmony_ci struct v4l2_subdev_format *fmt) 13098c2ecf20Sopenharmony_ci{ 13108c2ecf20Sopenharmony_ci struct v4l2_mbus_framefmt *mf = &fmt->format; 13118c2ecf20Sopenharmony_ci struct s5k5baf *state = to_s5k5baf(sd); 13128c2ecf20Sopenharmony_ci const struct s5k5baf_pixfmt *pixfmt; 13138c2ecf20Sopenharmony_ci int ret = 0; 13148c2ecf20Sopenharmony_ci 13158c2ecf20Sopenharmony_ci mf->field = V4L2_FIELD_NONE; 13168c2ecf20Sopenharmony_ci 13178c2ecf20Sopenharmony_ci if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { 13188c2ecf20Sopenharmony_ci *v4l2_subdev_get_try_format(sd, cfg, fmt->pad) = *mf; 13198c2ecf20Sopenharmony_ci return 0; 13208c2ecf20Sopenharmony_ci } 13218c2ecf20Sopenharmony_ci 13228c2ecf20Sopenharmony_ci if (fmt->pad == PAD_CIS) { 13238c2ecf20Sopenharmony_ci s5k5baf_try_cis_format(mf); 13248c2ecf20Sopenharmony_ci return 0; 13258c2ecf20Sopenharmony_ci } 13268c2ecf20Sopenharmony_ci 13278c2ecf20Sopenharmony_ci mutex_lock(&state->lock); 13288c2ecf20Sopenharmony_ci 13298c2ecf20Sopenharmony_ci if (state->streaming) { 13308c2ecf20Sopenharmony_ci mutex_unlock(&state->lock); 13318c2ecf20Sopenharmony_ci return -EBUSY; 13328c2ecf20Sopenharmony_ci } 13338c2ecf20Sopenharmony_ci 13348c2ecf20Sopenharmony_ci state->pixfmt = s5k5baf_try_isp_format(mf); 13358c2ecf20Sopenharmony_ci pixfmt = &s5k5baf_formats[state->pixfmt]; 13368c2ecf20Sopenharmony_ci mf->code = pixfmt->code; 13378c2ecf20Sopenharmony_ci mf->colorspace = pixfmt->colorspace; 13388c2ecf20Sopenharmony_ci mf->width = state->crop_source.width; 13398c2ecf20Sopenharmony_ci mf->height = state->crop_source.height; 13408c2ecf20Sopenharmony_ci 13418c2ecf20Sopenharmony_ci mutex_unlock(&state->lock); 13428c2ecf20Sopenharmony_ci return ret; 13438c2ecf20Sopenharmony_ci} 13448c2ecf20Sopenharmony_ci 13458c2ecf20Sopenharmony_cienum selection_rect { R_CIS, R_CROP_SINK, R_COMPOSE, R_CROP_SOURCE, R_INVALID }; 13468c2ecf20Sopenharmony_ci 13478c2ecf20Sopenharmony_cistatic enum selection_rect s5k5baf_get_sel_rect(u32 pad, u32 target) 13488c2ecf20Sopenharmony_ci{ 13498c2ecf20Sopenharmony_ci switch (target) { 13508c2ecf20Sopenharmony_ci case V4L2_SEL_TGT_CROP_BOUNDS: 13518c2ecf20Sopenharmony_ci return pad ? R_COMPOSE : R_CIS; 13528c2ecf20Sopenharmony_ci case V4L2_SEL_TGT_CROP: 13538c2ecf20Sopenharmony_ci return pad ? R_CROP_SOURCE : R_CROP_SINK; 13548c2ecf20Sopenharmony_ci case V4L2_SEL_TGT_COMPOSE_BOUNDS: 13558c2ecf20Sopenharmony_ci return pad ? R_INVALID : R_CROP_SINK; 13568c2ecf20Sopenharmony_ci case V4L2_SEL_TGT_COMPOSE: 13578c2ecf20Sopenharmony_ci return pad ? R_INVALID : R_COMPOSE; 13588c2ecf20Sopenharmony_ci default: 13598c2ecf20Sopenharmony_ci return R_INVALID; 13608c2ecf20Sopenharmony_ci } 13618c2ecf20Sopenharmony_ci} 13628c2ecf20Sopenharmony_ci 13638c2ecf20Sopenharmony_cistatic int s5k5baf_is_bound_target(u32 target) 13648c2ecf20Sopenharmony_ci{ 13658c2ecf20Sopenharmony_ci return target == V4L2_SEL_TGT_CROP_BOUNDS || 13668c2ecf20Sopenharmony_ci target == V4L2_SEL_TGT_COMPOSE_BOUNDS; 13678c2ecf20Sopenharmony_ci} 13688c2ecf20Sopenharmony_ci 13698c2ecf20Sopenharmony_cistatic int s5k5baf_get_selection(struct v4l2_subdev *sd, 13708c2ecf20Sopenharmony_ci struct v4l2_subdev_pad_config *cfg, 13718c2ecf20Sopenharmony_ci struct v4l2_subdev_selection *sel) 13728c2ecf20Sopenharmony_ci{ 13738c2ecf20Sopenharmony_ci enum selection_rect rtype; 13748c2ecf20Sopenharmony_ci struct s5k5baf *state = to_s5k5baf(sd); 13758c2ecf20Sopenharmony_ci 13768c2ecf20Sopenharmony_ci rtype = s5k5baf_get_sel_rect(sel->pad, sel->target); 13778c2ecf20Sopenharmony_ci 13788c2ecf20Sopenharmony_ci switch (rtype) { 13798c2ecf20Sopenharmony_ci case R_INVALID: 13808c2ecf20Sopenharmony_ci return -EINVAL; 13818c2ecf20Sopenharmony_ci case R_CIS: 13828c2ecf20Sopenharmony_ci sel->r = s5k5baf_cis_rect; 13838c2ecf20Sopenharmony_ci return 0; 13848c2ecf20Sopenharmony_ci default: 13858c2ecf20Sopenharmony_ci break; 13868c2ecf20Sopenharmony_ci } 13878c2ecf20Sopenharmony_ci 13888c2ecf20Sopenharmony_ci if (sel->which == V4L2_SUBDEV_FORMAT_TRY) { 13898c2ecf20Sopenharmony_ci if (rtype == R_COMPOSE) 13908c2ecf20Sopenharmony_ci sel->r = *v4l2_subdev_get_try_compose(sd, cfg, sel->pad); 13918c2ecf20Sopenharmony_ci else 13928c2ecf20Sopenharmony_ci sel->r = *v4l2_subdev_get_try_crop(sd, cfg, sel->pad); 13938c2ecf20Sopenharmony_ci return 0; 13948c2ecf20Sopenharmony_ci } 13958c2ecf20Sopenharmony_ci 13968c2ecf20Sopenharmony_ci mutex_lock(&state->lock); 13978c2ecf20Sopenharmony_ci switch (rtype) { 13988c2ecf20Sopenharmony_ci case R_CROP_SINK: 13998c2ecf20Sopenharmony_ci sel->r = state->crop_sink; 14008c2ecf20Sopenharmony_ci break; 14018c2ecf20Sopenharmony_ci case R_COMPOSE: 14028c2ecf20Sopenharmony_ci sel->r = state->compose; 14038c2ecf20Sopenharmony_ci break; 14048c2ecf20Sopenharmony_ci case R_CROP_SOURCE: 14058c2ecf20Sopenharmony_ci sel->r = state->crop_source; 14068c2ecf20Sopenharmony_ci break; 14078c2ecf20Sopenharmony_ci default: 14088c2ecf20Sopenharmony_ci break; 14098c2ecf20Sopenharmony_ci } 14108c2ecf20Sopenharmony_ci if (s5k5baf_is_bound_target(sel->target)) { 14118c2ecf20Sopenharmony_ci sel->r.left = 0; 14128c2ecf20Sopenharmony_ci sel->r.top = 0; 14138c2ecf20Sopenharmony_ci } 14148c2ecf20Sopenharmony_ci mutex_unlock(&state->lock); 14158c2ecf20Sopenharmony_ci 14168c2ecf20Sopenharmony_ci return 0; 14178c2ecf20Sopenharmony_ci} 14188c2ecf20Sopenharmony_ci 14198c2ecf20Sopenharmony_ci/* bounds range [start, start+len) to [0, max) and aligns to 2 */ 14208c2ecf20Sopenharmony_cistatic void s5k5baf_bound_range(u32 *start, u32 *len, u32 max) 14218c2ecf20Sopenharmony_ci{ 14228c2ecf20Sopenharmony_ci if (*len > max) 14238c2ecf20Sopenharmony_ci *len = max; 14248c2ecf20Sopenharmony_ci if (*start + *len > max) 14258c2ecf20Sopenharmony_ci *start = max - *len; 14268c2ecf20Sopenharmony_ci *start &= ~1; 14278c2ecf20Sopenharmony_ci *len &= ~1; 14288c2ecf20Sopenharmony_ci if (*len < S5K5BAF_WIN_WIDTH_MIN) 14298c2ecf20Sopenharmony_ci *len = S5K5BAF_WIN_WIDTH_MIN; 14308c2ecf20Sopenharmony_ci} 14318c2ecf20Sopenharmony_ci 14328c2ecf20Sopenharmony_cistatic void s5k5baf_bound_rect(struct v4l2_rect *r, u32 width, u32 height) 14338c2ecf20Sopenharmony_ci{ 14348c2ecf20Sopenharmony_ci s5k5baf_bound_range(&r->left, &r->width, width); 14358c2ecf20Sopenharmony_ci s5k5baf_bound_range(&r->top, &r->height, height); 14368c2ecf20Sopenharmony_ci} 14378c2ecf20Sopenharmony_ci 14388c2ecf20Sopenharmony_cistatic void s5k5baf_set_rect_and_adjust(struct v4l2_rect **rects, 14398c2ecf20Sopenharmony_ci enum selection_rect first, 14408c2ecf20Sopenharmony_ci struct v4l2_rect *v) 14418c2ecf20Sopenharmony_ci{ 14428c2ecf20Sopenharmony_ci struct v4l2_rect *r, *br; 14438c2ecf20Sopenharmony_ci enum selection_rect i = first; 14448c2ecf20Sopenharmony_ci 14458c2ecf20Sopenharmony_ci *rects[first] = *v; 14468c2ecf20Sopenharmony_ci do { 14478c2ecf20Sopenharmony_ci r = rects[i]; 14488c2ecf20Sopenharmony_ci br = rects[i - 1]; 14498c2ecf20Sopenharmony_ci s5k5baf_bound_rect(r, br->width, br->height); 14508c2ecf20Sopenharmony_ci } while (++i != R_INVALID); 14518c2ecf20Sopenharmony_ci *v = *rects[first]; 14528c2ecf20Sopenharmony_ci} 14538c2ecf20Sopenharmony_ci 14548c2ecf20Sopenharmony_cistatic bool s5k5baf_cmp_rect(const struct v4l2_rect *r1, 14558c2ecf20Sopenharmony_ci const struct v4l2_rect *r2) 14568c2ecf20Sopenharmony_ci{ 14578c2ecf20Sopenharmony_ci return !memcmp(r1, r2, sizeof(*r1)); 14588c2ecf20Sopenharmony_ci} 14598c2ecf20Sopenharmony_ci 14608c2ecf20Sopenharmony_cistatic int s5k5baf_set_selection(struct v4l2_subdev *sd, 14618c2ecf20Sopenharmony_ci struct v4l2_subdev_pad_config *cfg, 14628c2ecf20Sopenharmony_ci struct v4l2_subdev_selection *sel) 14638c2ecf20Sopenharmony_ci{ 14648c2ecf20Sopenharmony_ci static enum selection_rect rtype; 14658c2ecf20Sopenharmony_ci struct s5k5baf *state = to_s5k5baf(sd); 14668c2ecf20Sopenharmony_ci struct v4l2_rect **rects; 14678c2ecf20Sopenharmony_ci int ret = 0; 14688c2ecf20Sopenharmony_ci 14698c2ecf20Sopenharmony_ci rtype = s5k5baf_get_sel_rect(sel->pad, sel->target); 14708c2ecf20Sopenharmony_ci if (rtype == R_INVALID || s5k5baf_is_bound_target(sel->target)) 14718c2ecf20Sopenharmony_ci return -EINVAL; 14728c2ecf20Sopenharmony_ci 14738c2ecf20Sopenharmony_ci /* allow only scaling on compose */ 14748c2ecf20Sopenharmony_ci if (rtype == R_COMPOSE) { 14758c2ecf20Sopenharmony_ci sel->r.left = 0; 14768c2ecf20Sopenharmony_ci sel->r.top = 0; 14778c2ecf20Sopenharmony_ci } 14788c2ecf20Sopenharmony_ci 14798c2ecf20Sopenharmony_ci if (sel->which == V4L2_SUBDEV_FORMAT_TRY) { 14808c2ecf20Sopenharmony_ci rects = (struct v4l2_rect * []) { 14818c2ecf20Sopenharmony_ci &s5k5baf_cis_rect, 14828c2ecf20Sopenharmony_ci v4l2_subdev_get_try_crop(sd, cfg, PAD_CIS), 14838c2ecf20Sopenharmony_ci v4l2_subdev_get_try_compose(sd, cfg, PAD_CIS), 14848c2ecf20Sopenharmony_ci v4l2_subdev_get_try_crop(sd, cfg, PAD_OUT) 14858c2ecf20Sopenharmony_ci }; 14868c2ecf20Sopenharmony_ci s5k5baf_set_rect_and_adjust(rects, rtype, &sel->r); 14878c2ecf20Sopenharmony_ci return 0; 14888c2ecf20Sopenharmony_ci } 14898c2ecf20Sopenharmony_ci 14908c2ecf20Sopenharmony_ci rects = (struct v4l2_rect * []) { 14918c2ecf20Sopenharmony_ci &s5k5baf_cis_rect, 14928c2ecf20Sopenharmony_ci &state->crop_sink, 14938c2ecf20Sopenharmony_ci &state->compose, 14948c2ecf20Sopenharmony_ci &state->crop_source 14958c2ecf20Sopenharmony_ci }; 14968c2ecf20Sopenharmony_ci mutex_lock(&state->lock); 14978c2ecf20Sopenharmony_ci if (state->streaming) { 14988c2ecf20Sopenharmony_ci /* adjust sel->r to avoid output resolution change */ 14998c2ecf20Sopenharmony_ci if (rtype < R_CROP_SOURCE) { 15008c2ecf20Sopenharmony_ci if (sel->r.width < state->crop_source.width) 15018c2ecf20Sopenharmony_ci sel->r.width = state->crop_source.width; 15028c2ecf20Sopenharmony_ci if (sel->r.height < state->crop_source.height) 15038c2ecf20Sopenharmony_ci sel->r.height = state->crop_source.height; 15048c2ecf20Sopenharmony_ci } else { 15058c2ecf20Sopenharmony_ci sel->r.width = state->crop_source.width; 15068c2ecf20Sopenharmony_ci sel->r.height = state->crop_source.height; 15078c2ecf20Sopenharmony_ci } 15088c2ecf20Sopenharmony_ci } 15098c2ecf20Sopenharmony_ci s5k5baf_set_rect_and_adjust(rects, rtype, &sel->r); 15108c2ecf20Sopenharmony_ci if (!s5k5baf_cmp_rect(&state->crop_sink, &s5k5baf_cis_rect) || 15118c2ecf20Sopenharmony_ci !s5k5baf_cmp_rect(&state->compose, &s5k5baf_cis_rect)) 15128c2ecf20Sopenharmony_ci state->apply_crop = 1; 15138c2ecf20Sopenharmony_ci if (state->streaming) 15148c2ecf20Sopenharmony_ci ret = s5k5baf_hw_set_crop_rects(state); 15158c2ecf20Sopenharmony_ci mutex_unlock(&state->lock); 15168c2ecf20Sopenharmony_ci 15178c2ecf20Sopenharmony_ci return ret; 15188c2ecf20Sopenharmony_ci} 15198c2ecf20Sopenharmony_ci 15208c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_pad_ops s5k5baf_cis_pad_ops = { 15218c2ecf20Sopenharmony_ci .enum_mbus_code = s5k5baf_enum_mbus_code, 15228c2ecf20Sopenharmony_ci .enum_frame_size = s5k5baf_enum_frame_size, 15238c2ecf20Sopenharmony_ci .get_fmt = s5k5baf_get_fmt, 15248c2ecf20Sopenharmony_ci .set_fmt = s5k5baf_set_fmt, 15258c2ecf20Sopenharmony_ci}; 15268c2ecf20Sopenharmony_ci 15278c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_pad_ops s5k5baf_pad_ops = { 15288c2ecf20Sopenharmony_ci .enum_mbus_code = s5k5baf_enum_mbus_code, 15298c2ecf20Sopenharmony_ci .enum_frame_size = s5k5baf_enum_frame_size, 15308c2ecf20Sopenharmony_ci .enum_frame_interval = s5k5baf_enum_frame_interval, 15318c2ecf20Sopenharmony_ci .get_fmt = s5k5baf_get_fmt, 15328c2ecf20Sopenharmony_ci .set_fmt = s5k5baf_set_fmt, 15338c2ecf20Sopenharmony_ci .get_selection = s5k5baf_get_selection, 15348c2ecf20Sopenharmony_ci .set_selection = s5k5baf_set_selection, 15358c2ecf20Sopenharmony_ci}; 15368c2ecf20Sopenharmony_ci 15378c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_video_ops s5k5baf_video_ops = { 15388c2ecf20Sopenharmony_ci .g_frame_interval = s5k5baf_g_frame_interval, 15398c2ecf20Sopenharmony_ci .s_frame_interval = s5k5baf_s_frame_interval, 15408c2ecf20Sopenharmony_ci .s_stream = s5k5baf_s_stream, 15418c2ecf20Sopenharmony_ci}; 15428c2ecf20Sopenharmony_ci 15438c2ecf20Sopenharmony_ci/* 15448c2ecf20Sopenharmony_ci * V4L2 subdev controls 15458c2ecf20Sopenharmony_ci */ 15468c2ecf20Sopenharmony_ci 15478c2ecf20Sopenharmony_cistatic int s5k5baf_s_ctrl(struct v4l2_ctrl *ctrl) 15488c2ecf20Sopenharmony_ci{ 15498c2ecf20Sopenharmony_ci struct v4l2_subdev *sd = ctrl_to_sd(ctrl); 15508c2ecf20Sopenharmony_ci struct s5k5baf *state = to_s5k5baf(sd); 15518c2ecf20Sopenharmony_ci int ret; 15528c2ecf20Sopenharmony_ci 15538c2ecf20Sopenharmony_ci v4l2_dbg(1, debug, sd, "ctrl: %s, value: %d\n", ctrl->name, ctrl->val); 15548c2ecf20Sopenharmony_ci 15558c2ecf20Sopenharmony_ci mutex_lock(&state->lock); 15568c2ecf20Sopenharmony_ci 15578c2ecf20Sopenharmony_ci if (state->power == 0) 15588c2ecf20Sopenharmony_ci goto unlock; 15598c2ecf20Sopenharmony_ci 15608c2ecf20Sopenharmony_ci switch (ctrl->id) { 15618c2ecf20Sopenharmony_ci case V4L2_CID_AUTO_WHITE_BALANCE: 15628c2ecf20Sopenharmony_ci s5k5baf_hw_set_awb(state, ctrl->val); 15638c2ecf20Sopenharmony_ci break; 15648c2ecf20Sopenharmony_ci 15658c2ecf20Sopenharmony_ci case V4L2_CID_BRIGHTNESS: 15668c2ecf20Sopenharmony_ci s5k5baf_write(state, REG_USER_BRIGHTNESS, ctrl->val); 15678c2ecf20Sopenharmony_ci break; 15688c2ecf20Sopenharmony_ci 15698c2ecf20Sopenharmony_ci case V4L2_CID_COLORFX: 15708c2ecf20Sopenharmony_ci s5k5baf_hw_set_colorfx(state, ctrl->val); 15718c2ecf20Sopenharmony_ci break; 15728c2ecf20Sopenharmony_ci 15738c2ecf20Sopenharmony_ci case V4L2_CID_CONTRAST: 15748c2ecf20Sopenharmony_ci s5k5baf_write(state, REG_USER_CONTRAST, ctrl->val); 15758c2ecf20Sopenharmony_ci break; 15768c2ecf20Sopenharmony_ci 15778c2ecf20Sopenharmony_ci case V4L2_CID_EXPOSURE_AUTO: 15788c2ecf20Sopenharmony_ci s5k5baf_hw_set_auto_exposure(state, ctrl->val); 15798c2ecf20Sopenharmony_ci break; 15808c2ecf20Sopenharmony_ci 15818c2ecf20Sopenharmony_ci case V4L2_CID_HFLIP: 15828c2ecf20Sopenharmony_ci s5k5baf_hw_set_mirror(state); 15838c2ecf20Sopenharmony_ci break; 15848c2ecf20Sopenharmony_ci 15858c2ecf20Sopenharmony_ci case V4L2_CID_POWER_LINE_FREQUENCY: 15868c2ecf20Sopenharmony_ci s5k5baf_hw_set_anti_flicker(state, ctrl->val); 15878c2ecf20Sopenharmony_ci break; 15888c2ecf20Sopenharmony_ci 15898c2ecf20Sopenharmony_ci case V4L2_CID_SATURATION: 15908c2ecf20Sopenharmony_ci s5k5baf_write(state, REG_USER_SATURATION, ctrl->val); 15918c2ecf20Sopenharmony_ci break; 15928c2ecf20Sopenharmony_ci 15938c2ecf20Sopenharmony_ci case V4L2_CID_SHARPNESS: 15948c2ecf20Sopenharmony_ci s5k5baf_write(state, REG_USER_SHARPBLUR, ctrl->val); 15958c2ecf20Sopenharmony_ci break; 15968c2ecf20Sopenharmony_ci 15978c2ecf20Sopenharmony_ci case V4L2_CID_WHITE_BALANCE_TEMPERATURE: 15988c2ecf20Sopenharmony_ci s5k5baf_write(state, REG_P_COLORTEMP(0), ctrl->val); 15998c2ecf20Sopenharmony_ci if (state->apply_cfg) 16008c2ecf20Sopenharmony_ci s5k5baf_hw_sync_cfg(state); 16018c2ecf20Sopenharmony_ci break; 16028c2ecf20Sopenharmony_ci 16038c2ecf20Sopenharmony_ci case V4L2_CID_TEST_PATTERN: 16048c2ecf20Sopenharmony_ci s5k5baf_hw_set_test_pattern(state, ctrl->val); 16058c2ecf20Sopenharmony_ci break; 16068c2ecf20Sopenharmony_ci } 16078c2ecf20Sopenharmony_ciunlock: 16088c2ecf20Sopenharmony_ci ret = s5k5baf_clear_error(state); 16098c2ecf20Sopenharmony_ci mutex_unlock(&state->lock); 16108c2ecf20Sopenharmony_ci return ret; 16118c2ecf20Sopenharmony_ci} 16128c2ecf20Sopenharmony_ci 16138c2ecf20Sopenharmony_cistatic const struct v4l2_ctrl_ops s5k5baf_ctrl_ops = { 16148c2ecf20Sopenharmony_ci .s_ctrl = s5k5baf_s_ctrl, 16158c2ecf20Sopenharmony_ci}; 16168c2ecf20Sopenharmony_ci 16178c2ecf20Sopenharmony_cistatic const char * const s5k5baf_test_pattern_menu[] = { 16188c2ecf20Sopenharmony_ci "Disabled", 16198c2ecf20Sopenharmony_ci "Blank", 16208c2ecf20Sopenharmony_ci "Bars", 16218c2ecf20Sopenharmony_ci "Gradients", 16228c2ecf20Sopenharmony_ci "Textile", 16238c2ecf20Sopenharmony_ci "Textile2", 16248c2ecf20Sopenharmony_ci "Squares" 16258c2ecf20Sopenharmony_ci}; 16268c2ecf20Sopenharmony_ci 16278c2ecf20Sopenharmony_cistatic int s5k5baf_initialize_ctrls(struct s5k5baf *state) 16288c2ecf20Sopenharmony_ci{ 16298c2ecf20Sopenharmony_ci const struct v4l2_ctrl_ops *ops = &s5k5baf_ctrl_ops; 16308c2ecf20Sopenharmony_ci struct s5k5baf_ctrls *ctrls = &state->ctrls; 16318c2ecf20Sopenharmony_ci struct v4l2_ctrl_handler *hdl = &ctrls->handler; 16328c2ecf20Sopenharmony_ci int ret; 16338c2ecf20Sopenharmony_ci 16348c2ecf20Sopenharmony_ci ret = v4l2_ctrl_handler_init(hdl, 16); 16358c2ecf20Sopenharmony_ci if (ret < 0) { 16368c2ecf20Sopenharmony_ci v4l2_err(&state->sd, "cannot init ctrl handler (%d)\n", ret); 16378c2ecf20Sopenharmony_ci return ret; 16388c2ecf20Sopenharmony_ci } 16398c2ecf20Sopenharmony_ci 16408c2ecf20Sopenharmony_ci /* Auto white balance cluster */ 16418c2ecf20Sopenharmony_ci ctrls->awb = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_AUTO_WHITE_BALANCE, 16428c2ecf20Sopenharmony_ci 0, 1, 1, 1); 16438c2ecf20Sopenharmony_ci ctrls->gain_red = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_RED_BALANCE, 16448c2ecf20Sopenharmony_ci 0, 255, 1, S5K5BAF_GAIN_RED_DEF); 16458c2ecf20Sopenharmony_ci ctrls->gain_blue = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_BLUE_BALANCE, 16468c2ecf20Sopenharmony_ci 0, 255, 1, S5K5BAF_GAIN_BLUE_DEF); 16478c2ecf20Sopenharmony_ci v4l2_ctrl_auto_cluster(3, &ctrls->awb, 0, false); 16488c2ecf20Sopenharmony_ci 16498c2ecf20Sopenharmony_ci ctrls->hflip = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_HFLIP, 0, 1, 1, 0); 16508c2ecf20Sopenharmony_ci ctrls->vflip = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_VFLIP, 0, 1, 1, 0); 16518c2ecf20Sopenharmony_ci v4l2_ctrl_cluster(2, &ctrls->hflip); 16528c2ecf20Sopenharmony_ci 16538c2ecf20Sopenharmony_ci ctrls->auto_exp = v4l2_ctrl_new_std_menu(hdl, ops, 16548c2ecf20Sopenharmony_ci V4L2_CID_EXPOSURE_AUTO, 16558c2ecf20Sopenharmony_ci V4L2_EXPOSURE_MANUAL, 0, V4L2_EXPOSURE_AUTO); 16568c2ecf20Sopenharmony_ci /* Exposure time: x 1 us */ 16578c2ecf20Sopenharmony_ci ctrls->exposure = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_EXPOSURE, 16588c2ecf20Sopenharmony_ci 0, 6000000U, 1, 100000U); 16598c2ecf20Sopenharmony_ci /* Total gain: 256 <=> 1x */ 16608c2ecf20Sopenharmony_ci ctrls->gain = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_GAIN, 16618c2ecf20Sopenharmony_ci 0, 256, 1, 256); 16628c2ecf20Sopenharmony_ci v4l2_ctrl_auto_cluster(3, &ctrls->auto_exp, 0, false); 16638c2ecf20Sopenharmony_ci 16648c2ecf20Sopenharmony_ci v4l2_ctrl_new_std_menu(hdl, ops, V4L2_CID_POWER_LINE_FREQUENCY, 16658c2ecf20Sopenharmony_ci V4L2_CID_POWER_LINE_FREQUENCY_AUTO, 0, 16668c2ecf20Sopenharmony_ci V4L2_CID_POWER_LINE_FREQUENCY_AUTO); 16678c2ecf20Sopenharmony_ci 16688c2ecf20Sopenharmony_ci v4l2_ctrl_new_std_menu(hdl, ops, V4L2_CID_COLORFX, 16698c2ecf20Sopenharmony_ci V4L2_COLORFX_SKY_BLUE, ~0x6f, V4L2_COLORFX_NONE); 16708c2ecf20Sopenharmony_ci 16718c2ecf20Sopenharmony_ci v4l2_ctrl_new_std(hdl, ops, V4L2_CID_WHITE_BALANCE_TEMPERATURE, 16728c2ecf20Sopenharmony_ci 0, 256, 1, 0); 16738c2ecf20Sopenharmony_ci 16748c2ecf20Sopenharmony_ci v4l2_ctrl_new_std(hdl, ops, V4L2_CID_SATURATION, -127, 127, 1, 0); 16758c2ecf20Sopenharmony_ci v4l2_ctrl_new_std(hdl, ops, V4L2_CID_BRIGHTNESS, -127, 127, 1, 0); 16768c2ecf20Sopenharmony_ci v4l2_ctrl_new_std(hdl, ops, V4L2_CID_CONTRAST, -127, 127, 1, 0); 16778c2ecf20Sopenharmony_ci v4l2_ctrl_new_std(hdl, ops, V4L2_CID_SHARPNESS, -127, 127, 1, 0); 16788c2ecf20Sopenharmony_ci 16798c2ecf20Sopenharmony_ci v4l2_ctrl_new_std_menu_items(hdl, ops, V4L2_CID_TEST_PATTERN, 16808c2ecf20Sopenharmony_ci ARRAY_SIZE(s5k5baf_test_pattern_menu) - 1, 16818c2ecf20Sopenharmony_ci 0, 0, s5k5baf_test_pattern_menu); 16828c2ecf20Sopenharmony_ci 16838c2ecf20Sopenharmony_ci if (hdl->error) { 16848c2ecf20Sopenharmony_ci v4l2_err(&state->sd, "error creating controls (%d)\n", 16858c2ecf20Sopenharmony_ci hdl->error); 16868c2ecf20Sopenharmony_ci ret = hdl->error; 16878c2ecf20Sopenharmony_ci v4l2_ctrl_handler_free(hdl); 16888c2ecf20Sopenharmony_ci return ret; 16898c2ecf20Sopenharmony_ci } 16908c2ecf20Sopenharmony_ci 16918c2ecf20Sopenharmony_ci state->sd.ctrl_handler = hdl; 16928c2ecf20Sopenharmony_ci return 0; 16938c2ecf20Sopenharmony_ci} 16948c2ecf20Sopenharmony_ci 16958c2ecf20Sopenharmony_ci/* 16968c2ecf20Sopenharmony_ci * V4L2 subdev internal operations 16978c2ecf20Sopenharmony_ci */ 16988c2ecf20Sopenharmony_cistatic int s5k5baf_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) 16998c2ecf20Sopenharmony_ci{ 17008c2ecf20Sopenharmony_ci struct v4l2_mbus_framefmt *mf; 17018c2ecf20Sopenharmony_ci 17028c2ecf20Sopenharmony_ci mf = v4l2_subdev_get_try_format(sd, fh->pad, PAD_CIS); 17038c2ecf20Sopenharmony_ci s5k5baf_try_cis_format(mf); 17048c2ecf20Sopenharmony_ci 17058c2ecf20Sopenharmony_ci if (s5k5baf_is_cis_subdev(sd)) 17068c2ecf20Sopenharmony_ci return 0; 17078c2ecf20Sopenharmony_ci 17088c2ecf20Sopenharmony_ci mf = v4l2_subdev_get_try_format(sd, fh->pad, PAD_OUT); 17098c2ecf20Sopenharmony_ci mf->colorspace = s5k5baf_formats[0].colorspace; 17108c2ecf20Sopenharmony_ci mf->code = s5k5baf_formats[0].code; 17118c2ecf20Sopenharmony_ci mf->width = s5k5baf_cis_rect.width; 17128c2ecf20Sopenharmony_ci mf->height = s5k5baf_cis_rect.height; 17138c2ecf20Sopenharmony_ci mf->field = V4L2_FIELD_NONE; 17148c2ecf20Sopenharmony_ci 17158c2ecf20Sopenharmony_ci *v4l2_subdev_get_try_crop(sd, fh->pad, PAD_CIS) = s5k5baf_cis_rect; 17168c2ecf20Sopenharmony_ci *v4l2_subdev_get_try_compose(sd, fh->pad, PAD_CIS) = s5k5baf_cis_rect; 17178c2ecf20Sopenharmony_ci *v4l2_subdev_get_try_crop(sd, fh->pad, PAD_OUT) = s5k5baf_cis_rect; 17188c2ecf20Sopenharmony_ci 17198c2ecf20Sopenharmony_ci return 0; 17208c2ecf20Sopenharmony_ci} 17218c2ecf20Sopenharmony_ci 17228c2ecf20Sopenharmony_cistatic int s5k5baf_check_fw_revision(struct s5k5baf *state) 17238c2ecf20Sopenharmony_ci{ 17248c2ecf20Sopenharmony_ci u16 api_ver = 0, fw_rev = 0, s_id = 0; 17258c2ecf20Sopenharmony_ci int ret; 17268c2ecf20Sopenharmony_ci 17278c2ecf20Sopenharmony_ci api_ver = s5k5baf_read(state, REG_FW_APIVER); 17288c2ecf20Sopenharmony_ci fw_rev = s5k5baf_read(state, REG_FW_REVISION) & 0xff; 17298c2ecf20Sopenharmony_ci s_id = s5k5baf_read(state, REG_FW_SENSOR_ID); 17308c2ecf20Sopenharmony_ci ret = s5k5baf_clear_error(state); 17318c2ecf20Sopenharmony_ci if (ret < 0) 17328c2ecf20Sopenharmony_ci return ret; 17338c2ecf20Sopenharmony_ci 17348c2ecf20Sopenharmony_ci v4l2_info(&state->sd, "FW API=%#x, revision=%#x sensor_id=%#x\n", 17358c2ecf20Sopenharmony_ci api_ver, fw_rev, s_id); 17368c2ecf20Sopenharmony_ci 17378c2ecf20Sopenharmony_ci if (api_ver != S5K5BAF_FW_APIVER) { 17388c2ecf20Sopenharmony_ci v4l2_err(&state->sd, "FW API version not supported\n"); 17398c2ecf20Sopenharmony_ci return -ENODEV; 17408c2ecf20Sopenharmony_ci } 17418c2ecf20Sopenharmony_ci 17428c2ecf20Sopenharmony_ci return 0; 17438c2ecf20Sopenharmony_ci} 17448c2ecf20Sopenharmony_ci 17458c2ecf20Sopenharmony_cistatic int s5k5baf_registered(struct v4l2_subdev *sd) 17468c2ecf20Sopenharmony_ci{ 17478c2ecf20Sopenharmony_ci struct s5k5baf *state = to_s5k5baf(sd); 17488c2ecf20Sopenharmony_ci int ret; 17498c2ecf20Sopenharmony_ci 17508c2ecf20Sopenharmony_ci ret = v4l2_device_register_subdev(sd->v4l2_dev, &state->cis_sd); 17518c2ecf20Sopenharmony_ci if (ret < 0) 17528c2ecf20Sopenharmony_ci v4l2_err(sd, "failed to register subdev %s\n", 17538c2ecf20Sopenharmony_ci state->cis_sd.name); 17548c2ecf20Sopenharmony_ci else 17558c2ecf20Sopenharmony_ci ret = media_create_pad_link(&state->cis_sd.entity, PAD_CIS, 17568c2ecf20Sopenharmony_ci &state->sd.entity, PAD_CIS, 17578c2ecf20Sopenharmony_ci MEDIA_LNK_FL_IMMUTABLE | 17588c2ecf20Sopenharmony_ci MEDIA_LNK_FL_ENABLED); 17598c2ecf20Sopenharmony_ci return ret; 17608c2ecf20Sopenharmony_ci} 17618c2ecf20Sopenharmony_ci 17628c2ecf20Sopenharmony_cistatic void s5k5baf_unregistered(struct v4l2_subdev *sd) 17638c2ecf20Sopenharmony_ci{ 17648c2ecf20Sopenharmony_ci struct s5k5baf *state = to_s5k5baf(sd); 17658c2ecf20Sopenharmony_ci v4l2_device_unregister_subdev(&state->cis_sd); 17668c2ecf20Sopenharmony_ci} 17678c2ecf20Sopenharmony_ci 17688c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_ops s5k5baf_cis_subdev_ops = { 17698c2ecf20Sopenharmony_ci .pad = &s5k5baf_cis_pad_ops, 17708c2ecf20Sopenharmony_ci}; 17718c2ecf20Sopenharmony_ci 17728c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_internal_ops s5k5baf_cis_subdev_internal_ops = { 17738c2ecf20Sopenharmony_ci .open = s5k5baf_open, 17748c2ecf20Sopenharmony_ci}; 17758c2ecf20Sopenharmony_ci 17768c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_internal_ops s5k5baf_subdev_internal_ops = { 17778c2ecf20Sopenharmony_ci .registered = s5k5baf_registered, 17788c2ecf20Sopenharmony_ci .unregistered = s5k5baf_unregistered, 17798c2ecf20Sopenharmony_ci .open = s5k5baf_open, 17808c2ecf20Sopenharmony_ci}; 17818c2ecf20Sopenharmony_ci 17828c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_core_ops s5k5baf_core_ops = { 17838c2ecf20Sopenharmony_ci .s_power = s5k5baf_set_power, 17848c2ecf20Sopenharmony_ci .log_status = v4l2_ctrl_subdev_log_status, 17858c2ecf20Sopenharmony_ci}; 17868c2ecf20Sopenharmony_ci 17878c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_ops s5k5baf_subdev_ops = { 17888c2ecf20Sopenharmony_ci .core = &s5k5baf_core_ops, 17898c2ecf20Sopenharmony_ci .pad = &s5k5baf_pad_ops, 17908c2ecf20Sopenharmony_ci .video = &s5k5baf_video_ops, 17918c2ecf20Sopenharmony_ci}; 17928c2ecf20Sopenharmony_ci 17938c2ecf20Sopenharmony_cistatic int s5k5baf_configure_gpios(struct s5k5baf *state) 17948c2ecf20Sopenharmony_ci{ 17958c2ecf20Sopenharmony_ci static const char * const name[] = { "S5K5BAF_STBY", "S5K5BAF_RST" }; 17968c2ecf20Sopenharmony_ci struct i2c_client *c = v4l2_get_subdevdata(&state->sd); 17978c2ecf20Sopenharmony_ci struct s5k5baf_gpio *g = state->gpios; 17988c2ecf20Sopenharmony_ci int ret, i; 17998c2ecf20Sopenharmony_ci 18008c2ecf20Sopenharmony_ci for (i = 0; i < NUM_GPIOS; ++i) { 18018c2ecf20Sopenharmony_ci int flags = GPIOF_DIR_OUT; 18028c2ecf20Sopenharmony_ci if (g[i].level) 18038c2ecf20Sopenharmony_ci flags |= GPIOF_INIT_HIGH; 18048c2ecf20Sopenharmony_ci ret = devm_gpio_request_one(&c->dev, g[i].gpio, flags, name[i]); 18058c2ecf20Sopenharmony_ci if (ret < 0) { 18068c2ecf20Sopenharmony_ci v4l2_err(c, "failed to request gpio %s\n", name[i]); 18078c2ecf20Sopenharmony_ci return ret; 18088c2ecf20Sopenharmony_ci } 18098c2ecf20Sopenharmony_ci } 18108c2ecf20Sopenharmony_ci return 0; 18118c2ecf20Sopenharmony_ci} 18128c2ecf20Sopenharmony_ci 18138c2ecf20Sopenharmony_cistatic int s5k5baf_parse_gpios(struct s5k5baf_gpio *gpios, struct device *dev) 18148c2ecf20Sopenharmony_ci{ 18158c2ecf20Sopenharmony_ci static const char * const names[] = { 18168c2ecf20Sopenharmony_ci "stbyn-gpios", 18178c2ecf20Sopenharmony_ci "rstn-gpios", 18188c2ecf20Sopenharmony_ci }; 18198c2ecf20Sopenharmony_ci struct device_node *node = dev->of_node; 18208c2ecf20Sopenharmony_ci enum of_gpio_flags flags; 18218c2ecf20Sopenharmony_ci int ret, i; 18228c2ecf20Sopenharmony_ci 18238c2ecf20Sopenharmony_ci for (i = 0; i < NUM_GPIOS; ++i) { 18248c2ecf20Sopenharmony_ci ret = of_get_named_gpio_flags(node, names[i], 0, &flags); 18258c2ecf20Sopenharmony_ci if (ret < 0) { 18268c2ecf20Sopenharmony_ci dev_err(dev, "no %s GPIO pin provided\n", names[i]); 18278c2ecf20Sopenharmony_ci return ret; 18288c2ecf20Sopenharmony_ci } 18298c2ecf20Sopenharmony_ci gpios[i].gpio = ret; 18308c2ecf20Sopenharmony_ci gpios[i].level = !(flags & OF_GPIO_ACTIVE_LOW); 18318c2ecf20Sopenharmony_ci } 18328c2ecf20Sopenharmony_ci 18338c2ecf20Sopenharmony_ci return 0; 18348c2ecf20Sopenharmony_ci} 18358c2ecf20Sopenharmony_ci 18368c2ecf20Sopenharmony_cistatic int s5k5baf_parse_device_node(struct s5k5baf *state, struct device *dev) 18378c2ecf20Sopenharmony_ci{ 18388c2ecf20Sopenharmony_ci struct device_node *node = dev->of_node; 18398c2ecf20Sopenharmony_ci struct device_node *node_ep; 18408c2ecf20Sopenharmony_ci struct v4l2_fwnode_endpoint ep = { .bus_type = 0 }; 18418c2ecf20Sopenharmony_ci int ret; 18428c2ecf20Sopenharmony_ci 18438c2ecf20Sopenharmony_ci if (!node) { 18448c2ecf20Sopenharmony_ci dev_err(dev, "no device-tree node provided\n"); 18458c2ecf20Sopenharmony_ci return -EINVAL; 18468c2ecf20Sopenharmony_ci } 18478c2ecf20Sopenharmony_ci 18488c2ecf20Sopenharmony_ci ret = of_property_read_u32(node, "clock-frequency", 18498c2ecf20Sopenharmony_ci &state->mclk_frequency); 18508c2ecf20Sopenharmony_ci if (ret < 0) { 18518c2ecf20Sopenharmony_ci state->mclk_frequency = S5K5BAF_DEFAULT_MCLK_FREQ; 18528c2ecf20Sopenharmony_ci dev_info(dev, "using default %u Hz clock frequency\n", 18538c2ecf20Sopenharmony_ci state->mclk_frequency); 18548c2ecf20Sopenharmony_ci } 18558c2ecf20Sopenharmony_ci 18568c2ecf20Sopenharmony_ci ret = s5k5baf_parse_gpios(state->gpios, dev); 18578c2ecf20Sopenharmony_ci if (ret < 0) 18588c2ecf20Sopenharmony_ci return ret; 18598c2ecf20Sopenharmony_ci 18608c2ecf20Sopenharmony_ci node_ep = of_graph_get_next_endpoint(node, NULL); 18618c2ecf20Sopenharmony_ci if (!node_ep) { 18628c2ecf20Sopenharmony_ci dev_err(dev, "no endpoint defined at node %pOF\n", node); 18638c2ecf20Sopenharmony_ci return -EINVAL; 18648c2ecf20Sopenharmony_ci } 18658c2ecf20Sopenharmony_ci 18668c2ecf20Sopenharmony_ci ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(node_ep), &ep); 18678c2ecf20Sopenharmony_ci of_node_put(node_ep); 18688c2ecf20Sopenharmony_ci if (ret) 18698c2ecf20Sopenharmony_ci return ret; 18708c2ecf20Sopenharmony_ci 18718c2ecf20Sopenharmony_ci state->bus_type = ep.bus_type; 18728c2ecf20Sopenharmony_ci 18738c2ecf20Sopenharmony_ci switch (state->bus_type) { 18748c2ecf20Sopenharmony_ci case V4L2_MBUS_CSI2_DPHY: 18758c2ecf20Sopenharmony_ci state->nlanes = ep.bus.mipi_csi2.num_data_lanes; 18768c2ecf20Sopenharmony_ci break; 18778c2ecf20Sopenharmony_ci case V4L2_MBUS_PARALLEL: 18788c2ecf20Sopenharmony_ci break; 18798c2ecf20Sopenharmony_ci default: 18808c2ecf20Sopenharmony_ci dev_err(dev, "unsupported bus in endpoint defined at node %pOF\n", 18818c2ecf20Sopenharmony_ci node); 18828c2ecf20Sopenharmony_ci return -EINVAL; 18838c2ecf20Sopenharmony_ci } 18848c2ecf20Sopenharmony_ci 18858c2ecf20Sopenharmony_ci return 0; 18868c2ecf20Sopenharmony_ci} 18878c2ecf20Sopenharmony_ci 18888c2ecf20Sopenharmony_cistatic int s5k5baf_configure_subdevs(struct s5k5baf *state, 18898c2ecf20Sopenharmony_ci struct i2c_client *c) 18908c2ecf20Sopenharmony_ci{ 18918c2ecf20Sopenharmony_ci struct v4l2_subdev *sd; 18928c2ecf20Sopenharmony_ci int ret; 18938c2ecf20Sopenharmony_ci 18948c2ecf20Sopenharmony_ci sd = &state->cis_sd; 18958c2ecf20Sopenharmony_ci v4l2_subdev_init(sd, &s5k5baf_cis_subdev_ops); 18968c2ecf20Sopenharmony_ci sd->owner = THIS_MODULE; 18978c2ecf20Sopenharmony_ci v4l2_set_subdevdata(sd, state); 18988c2ecf20Sopenharmony_ci snprintf(sd->name, sizeof(sd->name), "S5K5BAF-CIS %d-%04x", 18998c2ecf20Sopenharmony_ci i2c_adapter_id(c->adapter), c->addr); 19008c2ecf20Sopenharmony_ci 19018c2ecf20Sopenharmony_ci sd->internal_ops = &s5k5baf_cis_subdev_internal_ops; 19028c2ecf20Sopenharmony_ci sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; 19038c2ecf20Sopenharmony_ci 19048c2ecf20Sopenharmony_ci state->cis_pad.flags = MEDIA_PAD_FL_SOURCE; 19058c2ecf20Sopenharmony_ci sd->entity.function = MEDIA_ENT_F_CAM_SENSOR; 19068c2ecf20Sopenharmony_ci ret = media_entity_pads_init(&sd->entity, NUM_CIS_PADS, &state->cis_pad); 19078c2ecf20Sopenharmony_ci if (ret < 0) 19088c2ecf20Sopenharmony_ci goto err; 19098c2ecf20Sopenharmony_ci 19108c2ecf20Sopenharmony_ci sd = &state->sd; 19118c2ecf20Sopenharmony_ci v4l2_i2c_subdev_init(sd, c, &s5k5baf_subdev_ops); 19128c2ecf20Sopenharmony_ci snprintf(sd->name, sizeof(sd->name), "S5K5BAF-ISP %d-%04x", 19138c2ecf20Sopenharmony_ci i2c_adapter_id(c->adapter), c->addr); 19148c2ecf20Sopenharmony_ci 19158c2ecf20Sopenharmony_ci sd->internal_ops = &s5k5baf_subdev_internal_ops; 19168c2ecf20Sopenharmony_ci sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; 19178c2ecf20Sopenharmony_ci 19188c2ecf20Sopenharmony_ci state->pads[PAD_CIS].flags = MEDIA_PAD_FL_SINK; 19198c2ecf20Sopenharmony_ci state->pads[PAD_OUT].flags = MEDIA_PAD_FL_SOURCE; 19208c2ecf20Sopenharmony_ci sd->entity.function = MEDIA_ENT_F_V4L2_SUBDEV_UNKNOWN; 19218c2ecf20Sopenharmony_ci ret = media_entity_pads_init(&sd->entity, NUM_ISP_PADS, state->pads); 19228c2ecf20Sopenharmony_ci 19238c2ecf20Sopenharmony_ci if (!ret) 19248c2ecf20Sopenharmony_ci return 0; 19258c2ecf20Sopenharmony_ci 19268c2ecf20Sopenharmony_ci media_entity_cleanup(&state->cis_sd.entity); 19278c2ecf20Sopenharmony_cierr: 19288c2ecf20Sopenharmony_ci dev_err(&c->dev, "cannot init media entity %s\n", sd->name); 19298c2ecf20Sopenharmony_ci return ret; 19308c2ecf20Sopenharmony_ci} 19318c2ecf20Sopenharmony_ci 19328c2ecf20Sopenharmony_cistatic int s5k5baf_configure_regulators(struct s5k5baf *state) 19338c2ecf20Sopenharmony_ci{ 19348c2ecf20Sopenharmony_ci struct i2c_client *c = v4l2_get_subdevdata(&state->sd); 19358c2ecf20Sopenharmony_ci int ret; 19368c2ecf20Sopenharmony_ci int i; 19378c2ecf20Sopenharmony_ci 19388c2ecf20Sopenharmony_ci for (i = 0; i < S5K5BAF_NUM_SUPPLIES; i++) 19398c2ecf20Sopenharmony_ci state->supplies[i].supply = s5k5baf_supply_names[i]; 19408c2ecf20Sopenharmony_ci 19418c2ecf20Sopenharmony_ci ret = devm_regulator_bulk_get(&c->dev, S5K5BAF_NUM_SUPPLIES, 19428c2ecf20Sopenharmony_ci state->supplies); 19438c2ecf20Sopenharmony_ci if (ret < 0) 19448c2ecf20Sopenharmony_ci v4l2_err(c, "failed to get regulators\n"); 19458c2ecf20Sopenharmony_ci return ret; 19468c2ecf20Sopenharmony_ci} 19478c2ecf20Sopenharmony_ci 19488c2ecf20Sopenharmony_cistatic int s5k5baf_probe(struct i2c_client *c) 19498c2ecf20Sopenharmony_ci{ 19508c2ecf20Sopenharmony_ci struct s5k5baf *state; 19518c2ecf20Sopenharmony_ci int ret; 19528c2ecf20Sopenharmony_ci 19538c2ecf20Sopenharmony_ci state = devm_kzalloc(&c->dev, sizeof(*state), GFP_KERNEL); 19548c2ecf20Sopenharmony_ci if (!state) 19558c2ecf20Sopenharmony_ci return -ENOMEM; 19568c2ecf20Sopenharmony_ci 19578c2ecf20Sopenharmony_ci mutex_init(&state->lock); 19588c2ecf20Sopenharmony_ci state->crop_sink = s5k5baf_cis_rect; 19598c2ecf20Sopenharmony_ci state->compose = s5k5baf_cis_rect; 19608c2ecf20Sopenharmony_ci state->crop_source = s5k5baf_cis_rect; 19618c2ecf20Sopenharmony_ci 19628c2ecf20Sopenharmony_ci ret = s5k5baf_parse_device_node(state, &c->dev); 19638c2ecf20Sopenharmony_ci if (ret < 0) 19648c2ecf20Sopenharmony_ci return ret; 19658c2ecf20Sopenharmony_ci 19668c2ecf20Sopenharmony_ci ret = s5k5baf_configure_subdevs(state, c); 19678c2ecf20Sopenharmony_ci if (ret < 0) 19688c2ecf20Sopenharmony_ci return ret; 19698c2ecf20Sopenharmony_ci 19708c2ecf20Sopenharmony_ci ret = s5k5baf_configure_gpios(state); 19718c2ecf20Sopenharmony_ci if (ret < 0) 19728c2ecf20Sopenharmony_ci goto err_me; 19738c2ecf20Sopenharmony_ci 19748c2ecf20Sopenharmony_ci ret = s5k5baf_configure_regulators(state); 19758c2ecf20Sopenharmony_ci if (ret < 0) 19768c2ecf20Sopenharmony_ci goto err_me; 19778c2ecf20Sopenharmony_ci 19788c2ecf20Sopenharmony_ci state->clock = devm_clk_get(state->sd.dev, S5K5BAF_CLK_NAME); 19798c2ecf20Sopenharmony_ci if (IS_ERR(state->clock)) { 19808c2ecf20Sopenharmony_ci ret = -EPROBE_DEFER; 19818c2ecf20Sopenharmony_ci goto err_me; 19828c2ecf20Sopenharmony_ci } 19838c2ecf20Sopenharmony_ci 19848c2ecf20Sopenharmony_ci ret = s5k5baf_power_on(state); 19858c2ecf20Sopenharmony_ci if (ret < 0) { 19868c2ecf20Sopenharmony_ci ret = -EPROBE_DEFER; 19878c2ecf20Sopenharmony_ci goto err_me; 19888c2ecf20Sopenharmony_ci } 19898c2ecf20Sopenharmony_ci s5k5baf_hw_init(state); 19908c2ecf20Sopenharmony_ci ret = s5k5baf_check_fw_revision(state); 19918c2ecf20Sopenharmony_ci 19928c2ecf20Sopenharmony_ci s5k5baf_power_off(state); 19938c2ecf20Sopenharmony_ci if (ret < 0) 19948c2ecf20Sopenharmony_ci goto err_me; 19958c2ecf20Sopenharmony_ci 19968c2ecf20Sopenharmony_ci ret = s5k5baf_initialize_ctrls(state); 19978c2ecf20Sopenharmony_ci if (ret < 0) 19988c2ecf20Sopenharmony_ci goto err_me; 19998c2ecf20Sopenharmony_ci 20008c2ecf20Sopenharmony_ci ret = v4l2_async_register_subdev(&state->sd); 20018c2ecf20Sopenharmony_ci if (ret < 0) 20028c2ecf20Sopenharmony_ci goto err_ctrl; 20038c2ecf20Sopenharmony_ci 20048c2ecf20Sopenharmony_ci return 0; 20058c2ecf20Sopenharmony_ci 20068c2ecf20Sopenharmony_cierr_ctrl: 20078c2ecf20Sopenharmony_ci v4l2_ctrl_handler_free(state->sd.ctrl_handler); 20088c2ecf20Sopenharmony_cierr_me: 20098c2ecf20Sopenharmony_ci media_entity_cleanup(&state->sd.entity); 20108c2ecf20Sopenharmony_ci media_entity_cleanup(&state->cis_sd.entity); 20118c2ecf20Sopenharmony_ci return ret; 20128c2ecf20Sopenharmony_ci} 20138c2ecf20Sopenharmony_ci 20148c2ecf20Sopenharmony_cistatic int s5k5baf_remove(struct i2c_client *c) 20158c2ecf20Sopenharmony_ci{ 20168c2ecf20Sopenharmony_ci struct v4l2_subdev *sd = i2c_get_clientdata(c); 20178c2ecf20Sopenharmony_ci struct s5k5baf *state = to_s5k5baf(sd); 20188c2ecf20Sopenharmony_ci 20198c2ecf20Sopenharmony_ci v4l2_async_unregister_subdev(sd); 20208c2ecf20Sopenharmony_ci v4l2_ctrl_handler_free(sd->ctrl_handler); 20218c2ecf20Sopenharmony_ci media_entity_cleanup(&sd->entity); 20228c2ecf20Sopenharmony_ci 20238c2ecf20Sopenharmony_ci sd = &state->cis_sd; 20248c2ecf20Sopenharmony_ci v4l2_device_unregister_subdev(sd); 20258c2ecf20Sopenharmony_ci media_entity_cleanup(&sd->entity); 20268c2ecf20Sopenharmony_ci 20278c2ecf20Sopenharmony_ci return 0; 20288c2ecf20Sopenharmony_ci} 20298c2ecf20Sopenharmony_ci 20308c2ecf20Sopenharmony_cistatic const struct i2c_device_id s5k5baf_id[] = { 20318c2ecf20Sopenharmony_ci { S5K5BAF_DRIVER_NAME, 0 }, 20328c2ecf20Sopenharmony_ci { }, 20338c2ecf20Sopenharmony_ci}; 20348c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, s5k5baf_id); 20358c2ecf20Sopenharmony_ci 20368c2ecf20Sopenharmony_cistatic const struct of_device_id s5k5baf_of_match[] = { 20378c2ecf20Sopenharmony_ci { .compatible = "samsung,s5k5baf" }, 20388c2ecf20Sopenharmony_ci { } 20398c2ecf20Sopenharmony_ci}; 20408c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, s5k5baf_of_match); 20418c2ecf20Sopenharmony_ci 20428c2ecf20Sopenharmony_cistatic struct i2c_driver s5k5baf_i2c_driver = { 20438c2ecf20Sopenharmony_ci .driver = { 20448c2ecf20Sopenharmony_ci .of_match_table = s5k5baf_of_match, 20458c2ecf20Sopenharmony_ci .name = S5K5BAF_DRIVER_NAME 20468c2ecf20Sopenharmony_ci }, 20478c2ecf20Sopenharmony_ci .probe_new = s5k5baf_probe, 20488c2ecf20Sopenharmony_ci .remove = s5k5baf_remove, 20498c2ecf20Sopenharmony_ci .id_table = s5k5baf_id, 20508c2ecf20Sopenharmony_ci}; 20518c2ecf20Sopenharmony_ci 20528c2ecf20Sopenharmony_cimodule_i2c_driver(s5k5baf_i2c_driver); 20538c2ecf20Sopenharmony_ci 20548c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Samsung S5K5BAF(X) UXGA camera driver"); 20558c2ecf20Sopenharmony_ciMODULE_AUTHOR("Andrzej Hajda <a.hajda@samsung.com>"); 20568c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 2057