17d459937SAndrzej Hajda /* 27d459937SAndrzej Hajda * Driver for Samsung S5K5BAF UXGA 1/5" 2M CMOS Image Sensor 37d459937SAndrzej Hajda * with embedded SoC ISP. 47d459937SAndrzej Hajda * 57d459937SAndrzej Hajda * Copyright (C) 2013, Samsung Electronics Co., Ltd. 67d459937SAndrzej Hajda * Andrzej Hajda <a.hajda@samsung.com> 77d459937SAndrzej Hajda * 87d459937SAndrzej Hajda * Based on S5K6AA driver authored by Sylwester Nawrocki 97d459937SAndrzej Hajda * Copyright (C) 2013, Samsung Electronics Co., Ltd. 107d459937SAndrzej Hajda * 117d459937SAndrzej Hajda * This program is free software; you can redistribute it and/or modify 127d459937SAndrzej Hajda * it under the terms of the GNU General Public License version 2 as 137d459937SAndrzej Hajda * published by the Free Software Foundation. 147d459937SAndrzej Hajda */ 157d459937SAndrzej Hajda 167d459937SAndrzej Hajda #include <linux/clk.h> 177d459937SAndrzej Hajda #include <linux/delay.h> 187d459937SAndrzej Hajda #include <linux/firmware.h> 197d459937SAndrzej Hajda #include <linux/gpio.h> 207d459937SAndrzej Hajda #include <linux/i2c.h> 217d459937SAndrzej Hajda #include <linux/media.h> 227d459937SAndrzej Hajda #include <linux/module.h> 237d459937SAndrzej Hajda #include <linux/of_gpio.h> 24fd9fdb78SPhilipp Zabel #include <linux/of_graph.h> 257d459937SAndrzej Hajda #include <linux/regulator/consumer.h> 267d459937SAndrzej Hajda #include <linux/slab.h> 277d459937SAndrzej Hajda 287d459937SAndrzej Hajda #include <media/media-entity.h> 297d459937SAndrzej Hajda #include <media/v4l2-ctrls.h> 307d459937SAndrzej Hajda #include <media/v4l2-device.h> 317d459937SAndrzej Hajda #include <media/v4l2-subdev.h> 327d459937SAndrzej Hajda #include <media/v4l2-mediabus.h> 33859969b3SSakari Ailus #include <media/v4l2-fwnode.h> 347d459937SAndrzej Hajda 357d459937SAndrzej Hajda static int debug; 367d459937SAndrzej Hajda module_param(debug, int, 0644); 377d459937SAndrzej Hajda 387d459937SAndrzej Hajda #define S5K5BAF_DRIVER_NAME "s5k5baf" 397d459937SAndrzej Hajda #define S5K5BAF_DEFAULT_MCLK_FREQ 24000000U 407d459937SAndrzej Hajda #define S5K5BAF_CLK_NAME "mclk" 417d459937SAndrzej Hajda 427d459937SAndrzej Hajda #define S5K5BAF_FW_FILENAME "s5k5baf-cfg.bin" 437d459937SAndrzej Hajda #define S5K5BAF_FW_TAG "SF00" 447d459937SAndrzej Hajda #define S5K5BAG_FW_TAG_LEN 2 457d459937SAndrzej Hajda #define S5K5BAG_FW_MAX_COUNT 16 467d459937SAndrzej Hajda 477d459937SAndrzej Hajda #define S5K5BAF_CIS_WIDTH 1600 487d459937SAndrzej Hajda #define S5K5BAF_CIS_HEIGHT 1200 497d459937SAndrzej Hajda #define S5K5BAF_WIN_WIDTH_MIN 8 507d459937SAndrzej Hajda #define S5K5BAF_WIN_HEIGHT_MIN 8 517d459937SAndrzej Hajda #define S5K5BAF_GAIN_RED_DEF 127 527d459937SAndrzej Hajda #define S5K5BAF_GAIN_GREEN_DEF 95 537d459937SAndrzej Hajda #define S5K5BAF_GAIN_BLUE_DEF 180 547d459937SAndrzej Hajda /* Default number of MIPI CSI-2 data lanes used */ 557d459937SAndrzej Hajda #define S5K5BAF_DEF_NUM_LANES 1 567d459937SAndrzej Hajda 577d459937SAndrzej Hajda #define AHB_MSB_ADDR_PTR 0xfcfc 587d459937SAndrzej Hajda 597d459937SAndrzej Hajda /* 607d459937SAndrzej Hajda * Register interface pages (the most significant word of the address) 617d459937SAndrzej Hajda */ 627d459937SAndrzej Hajda #define PAGE_IF_HW 0xd000 637d459937SAndrzej Hajda #define PAGE_IF_SW 0x7000 647d459937SAndrzej Hajda 657d459937SAndrzej Hajda /* 667d459937SAndrzej Hajda * H/W register Interface (PAGE_IF_HW) 677d459937SAndrzej Hajda */ 687d459937SAndrzej Hajda #define REG_SW_LOAD_COMPLETE 0x0014 697d459937SAndrzej Hajda #define REG_CMDWR_PAGE 0x0028 707d459937SAndrzej Hajda #define REG_CMDWR_ADDR 0x002a 717d459937SAndrzej Hajda #define REG_CMDRD_PAGE 0x002c 727d459937SAndrzej Hajda #define REG_CMDRD_ADDR 0x002e 737d459937SAndrzej Hajda #define REG_CMD_BUF 0x0f12 747d459937SAndrzej Hajda #define REG_SET_HOST_INT 0x1000 757d459937SAndrzej Hajda #define REG_CLEAR_HOST_INT 0x1030 767d459937SAndrzej Hajda #define REG_PATTERN_SET 0x3100 777d459937SAndrzej Hajda #define REG_PATTERN_WIDTH 0x3118 787d459937SAndrzej Hajda #define REG_PATTERN_HEIGHT 0x311a 797d459937SAndrzej Hajda #define REG_PATTERN_PARAM 0x311c 807d459937SAndrzej Hajda 817d459937SAndrzej Hajda /* 827d459937SAndrzej Hajda * S/W register interface (PAGE_IF_SW) 837d459937SAndrzej Hajda */ 847d459937SAndrzej Hajda 857d459937SAndrzej Hajda /* Firmware revision information */ 867d459937SAndrzej Hajda #define REG_FW_APIVER 0x012e 877d459937SAndrzej Hajda #define S5K5BAF_FW_APIVER 0x0001 887d459937SAndrzej Hajda #define REG_FW_REVISION 0x0130 897d459937SAndrzej Hajda #define REG_FW_SENSOR_ID 0x0152 907d459937SAndrzej Hajda 917d459937SAndrzej Hajda /* Initialization parameters */ 927d459937SAndrzej Hajda /* Master clock frequency in KHz */ 937d459937SAndrzej Hajda #define REG_I_INCLK_FREQ_L 0x01b8 947d459937SAndrzej Hajda #define REG_I_INCLK_FREQ_H 0x01ba 957d459937SAndrzej Hajda #define MIN_MCLK_FREQ_KHZ 6000U 967d459937SAndrzej Hajda #define MAX_MCLK_FREQ_KHZ 48000U 977d459937SAndrzej Hajda #define REG_I_USE_NPVI_CLOCKS 0x01c6 987d459937SAndrzej Hajda #define NPVI_CLOCKS 1 997d459937SAndrzej Hajda #define REG_I_USE_NMIPI_CLOCKS 0x01c8 1007d459937SAndrzej Hajda #define NMIPI_CLOCKS 1 1017d459937SAndrzej Hajda #define REG_I_BLOCK_INTERNAL_PLL_CALC 0x01ca 1027d459937SAndrzej Hajda 1037d459937SAndrzej Hajda /* Clock configurations, n = 0..2. REG_I_* frequency unit is 4 kHz. */ 1047d459937SAndrzej Hajda #define REG_I_OPCLK_4KHZ(n) ((n) * 6 + 0x01cc) 1057d459937SAndrzej Hajda #define REG_I_MIN_OUTRATE_4KHZ(n) ((n) * 6 + 0x01ce) 1067d459937SAndrzej Hajda #define REG_I_MAX_OUTRATE_4KHZ(n) ((n) * 6 + 0x01d0) 1077d459937SAndrzej Hajda #define SCLK_PVI_FREQ 24000 1087d459937SAndrzej Hajda #define SCLK_MIPI_FREQ 48000 1097d459937SAndrzej Hajda #define PCLK_MIN_FREQ 6000 1107d459937SAndrzej Hajda #define PCLK_MAX_FREQ 48000 1117d459937SAndrzej Hajda #define REG_I_USE_REGS_API 0x01de 1127d459937SAndrzej Hajda #define REG_I_INIT_PARAMS_UPDATED 0x01e0 1137d459937SAndrzej Hajda #define REG_I_ERROR_INFO 0x01e2 1147d459937SAndrzej Hajda 1157d459937SAndrzej Hajda /* General purpose parameters */ 1167d459937SAndrzej Hajda #define REG_USER_BRIGHTNESS 0x01e4 1177d459937SAndrzej Hajda #define REG_USER_CONTRAST 0x01e6 1187d459937SAndrzej Hajda #define REG_USER_SATURATION 0x01e8 1197d459937SAndrzej Hajda #define REG_USER_SHARPBLUR 0x01ea 1207d459937SAndrzej Hajda 1217d459937SAndrzej Hajda #define REG_G_SPEC_EFFECTS 0x01ee 1227d459937SAndrzej Hajda #define REG_G_ENABLE_PREV 0x01f0 1237d459937SAndrzej Hajda #define REG_G_ENABLE_PREV_CHG 0x01f2 1247d459937SAndrzej Hajda #define REG_G_NEW_CFG_SYNC 0x01f8 1257d459937SAndrzej Hajda #define REG_G_PREVREQ_IN_WIDTH 0x01fa 1267d459937SAndrzej Hajda #define REG_G_PREVREQ_IN_HEIGHT 0x01fc 1277d459937SAndrzej Hajda #define REG_G_PREVREQ_IN_XOFFS 0x01fe 1287d459937SAndrzej Hajda #define REG_G_PREVREQ_IN_YOFFS 0x0200 1297d459937SAndrzej Hajda #define REG_G_PREVZOOM_IN_WIDTH 0x020a 1307d459937SAndrzej Hajda #define REG_G_PREVZOOM_IN_HEIGHT 0x020c 1317d459937SAndrzej Hajda #define REG_G_PREVZOOM_IN_XOFFS 0x020e 1327d459937SAndrzej Hajda #define REG_G_PREVZOOM_IN_YOFFS 0x0210 1337d459937SAndrzej Hajda #define REG_G_INPUTS_CHANGE_REQ 0x021a 1347d459937SAndrzej Hajda #define REG_G_ACTIVE_PREV_CFG 0x021c 1357d459937SAndrzej Hajda #define REG_G_PREV_CFG_CHG 0x021e 1367d459937SAndrzej Hajda #define REG_G_PREV_OPEN_AFTER_CH 0x0220 1377d459937SAndrzej Hajda #define REG_G_PREV_CFG_ERROR 0x0222 1387d459937SAndrzej Hajda #define CFG_ERROR_RANGE 0x0b 1397d459937SAndrzej Hajda #define REG_G_PREV_CFG_BYPASS_CHANGED 0x022a 1407d459937SAndrzej Hajda #define REG_G_ACTUAL_P_FR_TIME 0x023a 1417d459937SAndrzej Hajda #define REG_G_ACTUAL_P_OUT_RATE 0x023c 1427d459937SAndrzej Hajda #define REG_G_ACTUAL_C_FR_TIME 0x023e 1437d459937SAndrzej Hajda #define REG_G_ACTUAL_C_OUT_RATE 0x0240 1447d459937SAndrzej Hajda 1457d459937SAndrzej Hajda /* Preview control section. n = 0...4. */ 1467d459937SAndrzej Hajda #define PREG(n, x) ((n) * 0x26 + x) 1477d459937SAndrzej Hajda #define REG_P_OUT_WIDTH(n) PREG(n, 0x0242) 1487d459937SAndrzej Hajda #define REG_P_OUT_HEIGHT(n) PREG(n, 0x0244) 1497d459937SAndrzej Hajda #define REG_P_FMT(n) PREG(n, 0x0246) 1507d459937SAndrzej Hajda #define REG_P_MAX_OUT_RATE(n) PREG(n, 0x0248) 1517d459937SAndrzej Hajda #define REG_P_MIN_OUT_RATE(n) PREG(n, 0x024a) 1527d459937SAndrzej Hajda #define REG_P_PVI_MASK(n) PREG(n, 0x024c) 1537d459937SAndrzej Hajda #define PVI_MASK_MIPI 0x52 1547d459937SAndrzej Hajda #define REG_P_CLK_INDEX(n) PREG(n, 0x024e) 1557d459937SAndrzej Hajda #define CLK_PVI_INDEX 0 1567d459937SAndrzej Hajda #define CLK_MIPI_INDEX NPVI_CLOCKS 1577d459937SAndrzej Hajda #define REG_P_FR_RATE_TYPE(n) PREG(n, 0x0250) 1587d459937SAndrzej Hajda #define FR_RATE_DYNAMIC 0 1597d459937SAndrzej Hajda #define FR_RATE_FIXED 1 1607d459937SAndrzej Hajda #define FR_RATE_FIXED_ACCURATE 2 1617d459937SAndrzej Hajda #define REG_P_FR_RATE_Q_TYPE(n) PREG(n, 0x0252) 1627d459937SAndrzej Hajda #define FR_RATE_Q_DYNAMIC 0 1637d459937SAndrzej Hajda #define FR_RATE_Q_BEST_FRRATE 1 /* Binning enabled */ 1647d459937SAndrzej Hajda #define FR_RATE_Q_BEST_QUALITY 2 /* Binning disabled */ 1657d459937SAndrzej Hajda /* Frame period in 0.1 ms units */ 1667d459937SAndrzej Hajda #define REG_P_MAX_FR_TIME(n) PREG(n, 0x0254) 1677d459937SAndrzej Hajda #define REG_P_MIN_FR_TIME(n) PREG(n, 0x0256) 1687d459937SAndrzej Hajda #define S5K5BAF_MIN_FR_TIME 333 /* x100 us */ 1697d459937SAndrzej Hajda #define S5K5BAF_MAX_FR_TIME 6500 /* x100 us */ 1707d459937SAndrzej Hajda /* The below 5 registers are for "device correction" values */ 1717d459937SAndrzej Hajda #define REG_P_SATURATION(n) PREG(n, 0x0258) 1727d459937SAndrzej Hajda #define REG_P_SHARP_BLUR(n) PREG(n, 0x025a) 1737d459937SAndrzej Hajda #define REG_P_GLAMOUR(n) PREG(n, 0x025c) 1747d459937SAndrzej Hajda #define REG_P_COLORTEMP(n) PREG(n, 0x025e) 1757d459937SAndrzej Hajda #define REG_P_GAMMA_INDEX(n) PREG(n, 0x0260) 1767d459937SAndrzej Hajda #define REG_P_PREV_MIRROR(n) PREG(n, 0x0262) 1777d459937SAndrzej Hajda #define REG_P_CAP_MIRROR(n) PREG(n, 0x0264) 1787d459937SAndrzej Hajda #define REG_P_CAP_ROTATION(n) PREG(n, 0x0266) 1797d459937SAndrzej Hajda 1807d459937SAndrzej Hajda /* Extended image property controls */ 1817d459937SAndrzej Hajda /* Exposure time in 10 us units */ 1827d459937SAndrzej Hajda #define REG_SF_USR_EXPOSURE_L 0x03bc 1837d459937SAndrzej Hajda #define REG_SF_USR_EXPOSURE_H 0x03be 1847d459937SAndrzej Hajda #define REG_SF_USR_EXPOSURE_CHG 0x03c0 1857d459937SAndrzej Hajda #define REG_SF_USR_TOT_GAIN 0x03c2 1867d459937SAndrzej Hajda #define REG_SF_USR_TOT_GAIN_CHG 0x03c4 1877d459937SAndrzej Hajda #define REG_SF_RGAIN 0x03c6 1887d459937SAndrzej Hajda #define REG_SF_RGAIN_CHG 0x03c8 1897d459937SAndrzej Hajda #define REG_SF_GGAIN 0x03ca 1907d459937SAndrzej Hajda #define REG_SF_GGAIN_CHG 0x03cc 1917d459937SAndrzej Hajda #define REG_SF_BGAIN 0x03ce 1927d459937SAndrzej Hajda #define REG_SF_BGAIN_CHG 0x03d0 1937d459937SAndrzej Hajda #define REG_SF_WBGAIN_CHG 0x03d2 1947d459937SAndrzej Hajda #define REG_SF_FLICKER_QUANT 0x03d4 1957d459937SAndrzej Hajda #define REG_SF_FLICKER_QUANT_CHG 0x03d6 1967d459937SAndrzej Hajda 1977d459937SAndrzej Hajda /* Output interface (parallel/MIPI) setup */ 1987d459937SAndrzej Hajda #define REG_OIF_EN_MIPI_LANES 0x03f2 1997d459937SAndrzej Hajda #define REG_OIF_EN_PACKETS 0x03f4 2007d459937SAndrzej Hajda #define EN_PACKETS_CSI2 0xc3 2017d459937SAndrzej Hajda #define REG_OIF_CFG_CHG 0x03f6 2027d459937SAndrzej Hajda 2037d459937SAndrzej Hajda /* Auto-algorithms enable mask */ 2047d459937SAndrzej Hajda #define REG_DBG_AUTOALG_EN 0x03f8 2057d459937SAndrzej Hajda #define AALG_ALL_EN BIT(0) 2067d459937SAndrzej Hajda #define AALG_AE_EN BIT(1) 2077d459937SAndrzej Hajda #define AALG_DIVLEI_EN BIT(2) 2087d459937SAndrzej Hajda #define AALG_WB_EN BIT(3) 2097d459937SAndrzej Hajda #define AALG_USE_WB_FOR_ISP BIT(4) 2107d459937SAndrzej Hajda #define AALG_FLICKER_EN BIT(5) 2117d459937SAndrzej Hajda #define AALG_FIT_EN BIT(6) 2127d459937SAndrzej Hajda #define AALG_WRHW_EN BIT(7) 2137d459937SAndrzej Hajda 2147d459937SAndrzej Hajda /* Pointers to color correction matrices */ 2157d459937SAndrzej Hajda #define REG_PTR_CCM_HORIZON 0x06d0 2167d459937SAndrzej Hajda #define REG_PTR_CCM_INCANDESCENT 0x06d4 2177d459937SAndrzej Hajda #define REG_PTR_CCM_WARM_WHITE 0x06d8 2187d459937SAndrzej Hajda #define REG_PTR_CCM_COOL_WHITE 0x06dc 2197d459937SAndrzej Hajda #define REG_PTR_CCM_DL50 0x06e0 2207d459937SAndrzej Hajda #define REG_PTR_CCM_DL65 0x06e4 2217d459937SAndrzej Hajda #define REG_PTR_CCM_OUTDOOR 0x06ec 2227d459937SAndrzej Hajda 2237d459937SAndrzej Hajda #define REG_ARR_CCM(n) (0x2800 + 36 * (n)) 2247d459937SAndrzej Hajda 2257d459937SAndrzej Hajda static const char * const s5k5baf_supply_names[] = { 2267d459937SAndrzej Hajda "vdda", /* Analog power supply 2.8V (2.6V to 3.0V) */ 2277d459937SAndrzej Hajda "vddreg", /* Regulator input power supply 1.8V (1.7V to 1.9V) 2287d459937SAndrzej Hajda or 2.8V (2.6V to 3.0) */ 2297d459937SAndrzej Hajda "vddio", /* I/O power supply 1.8V (1.65V to 1.95V) 2307d459937SAndrzej Hajda or 2.8V (2.5V to 3.1V) */ 2317d459937SAndrzej Hajda }; 2327d459937SAndrzej Hajda #define S5K5BAF_NUM_SUPPLIES ARRAY_SIZE(s5k5baf_supply_names) 2337d459937SAndrzej Hajda 2347d459937SAndrzej Hajda struct s5k5baf_gpio { 2357d459937SAndrzej Hajda int gpio; 2367d459937SAndrzej Hajda int level; 2377d459937SAndrzej Hajda }; 2387d459937SAndrzej Hajda 2397d459937SAndrzej Hajda enum s5k5baf_gpio_id { 2407d459937SAndrzej Hajda STBY, 2417d459937SAndrzej Hajda RST, 2427d459937SAndrzej Hajda NUM_GPIOS, 2437d459937SAndrzej Hajda }; 2447d459937SAndrzej Hajda 2457d459937SAndrzej Hajda #define PAD_CIS 0 2467d459937SAndrzej Hajda #define PAD_OUT 1 2477d459937SAndrzej Hajda #define NUM_CIS_PADS 1 2487d459937SAndrzej Hajda #define NUM_ISP_PADS 2 2497d459937SAndrzej Hajda 2507d459937SAndrzej Hajda struct s5k5baf_pixfmt { 251f5fe58fdSBoris BREZILLON u32 code; 2527d459937SAndrzej Hajda u32 colorspace; 2537d459937SAndrzej Hajda /* REG_P_FMT(x) register value */ 2547d459937SAndrzej Hajda u16 reg_p_fmt; 2557d459937SAndrzej Hajda }; 2567d459937SAndrzej Hajda 2577d459937SAndrzej Hajda struct s5k5baf_ctrls { 2587d459937SAndrzej Hajda struct v4l2_ctrl_handler handler; 2597d459937SAndrzej Hajda struct { /* Auto / manual white balance cluster */ 2607d459937SAndrzej Hajda struct v4l2_ctrl *awb; 2617d459937SAndrzej Hajda struct v4l2_ctrl *gain_red; 2627d459937SAndrzej Hajda struct v4l2_ctrl *gain_blue; 2637d459937SAndrzej Hajda }; 2647d459937SAndrzej Hajda struct { /* Mirror cluster */ 2657d459937SAndrzej Hajda struct v4l2_ctrl *hflip; 2667d459937SAndrzej Hajda struct v4l2_ctrl *vflip; 2677d459937SAndrzej Hajda }; 2687d459937SAndrzej Hajda struct { /* Auto exposure / manual exposure and gain cluster */ 2697d459937SAndrzej Hajda struct v4l2_ctrl *auto_exp; 2707d459937SAndrzej Hajda struct v4l2_ctrl *exposure; 2717d459937SAndrzej Hajda struct v4l2_ctrl *gain; 2727d459937SAndrzej Hajda }; 2737d459937SAndrzej Hajda }; 2747d459937SAndrzej Hajda 2757d459937SAndrzej Hajda enum { 2767d459937SAndrzej Hajda S5K5BAF_FW_ID_PATCH, 2777d459937SAndrzej Hajda S5K5BAF_FW_ID_CCM, 2787d459937SAndrzej Hajda S5K5BAF_FW_ID_CIS, 2797d459937SAndrzej Hajda }; 2807d459937SAndrzej Hajda 2817d459937SAndrzej Hajda struct s5k5baf_fw { 2827d459937SAndrzej Hajda u16 count; 2837d459937SAndrzej Hajda struct { 2847d459937SAndrzej Hajda u16 id; 2857d459937SAndrzej Hajda u16 offset; 2867d459937SAndrzej Hajda } seq[0]; 2877d459937SAndrzej Hajda u16 data[0]; 2887d459937SAndrzej Hajda }; 2897d459937SAndrzej Hajda 2907d459937SAndrzej Hajda struct s5k5baf { 2917d459937SAndrzej Hajda struct s5k5baf_gpio gpios[NUM_GPIOS]; 2927d459937SAndrzej Hajda enum v4l2_mbus_type bus_type; 2937d459937SAndrzej Hajda u8 nlanes; 2947d459937SAndrzej Hajda struct regulator_bulk_data supplies[S5K5BAF_NUM_SUPPLIES]; 2957d459937SAndrzej Hajda 2967d459937SAndrzej Hajda struct clk *clock; 2977d459937SAndrzej Hajda u32 mclk_frequency; 2987d459937SAndrzej Hajda 2997d459937SAndrzej Hajda struct s5k5baf_fw *fw; 3007d459937SAndrzej Hajda 3017d459937SAndrzej Hajda struct v4l2_subdev cis_sd; 3027d459937SAndrzej Hajda struct media_pad cis_pad; 3037d459937SAndrzej Hajda 3047d459937SAndrzej Hajda struct v4l2_subdev sd; 3057d459937SAndrzej Hajda struct media_pad pads[NUM_ISP_PADS]; 3067d459937SAndrzej Hajda 3077d459937SAndrzej Hajda /* protects the struct members below */ 3087d459937SAndrzej Hajda struct mutex lock; 3097d459937SAndrzej Hajda 3107d459937SAndrzej Hajda int error; 3117d459937SAndrzej Hajda 3127d459937SAndrzej Hajda struct v4l2_rect crop_sink; 3137d459937SAndrzej Hajda struct v4l2_rect compose; 3147d459937SAndrzej Hajda struct v4l2_rect crop_source; 3157d459937SAndrzej Hajda /* index to s5k5baf_formats array */ 3167d459937SAndrzej Hajda int pixfmt; 3177d459937SAndrzej Hajda /* actual frame interval in 100us */ 3187d459937SAndrzej Hajda u16 fiv; 3197d459937SAndrzej Hajda /* requested frame interval in 100us */ 3207d459937SAndrzej Hajda u16 req_fiv; 3217d459937SAndrzej Hajda /* cache for REG_DBG_AUTOALG_EN register */ 3227d459937SAndrzej Hajda u16 auto_alg; 3237d459937SAndrzej Hajda 3247d459937SAndrzej Hajda struct s5k5baf_ctrls ctrls; 3257d459937SAndrzej Hajda 3267d459937SAndrzej Hajda unsigned int streaming:1; 3277d459937SAndrzej Hajda unsigned int apply_cfg:1; 3287d459937SAndrzej Hajda unsigned int apply_crop:1; 3297d459937SAndrzej Hajda unsigned int valid_auto_alg:1; 3307d459937SAndrzej Hajda unsigned int power; 3317d459937SAndrzej Hajda }; 3327d459937SAndrzej Hajda 3337d459937SAndrzej Hajda static const struct s5k5baf_pixfmt s5k5baf_formats[] = { 334f5fe58fdSBoris BREZILLON { MEDIA_BUS_FMT_VYUY8_2X8, V4L2_COLORSPACE_JPEG, 5 }, 3357d459937SAndrzej Hajda /* range 16-240 */ 336f5fe58fdSBoris BREZILLON { MEDIA_BUS_FMT_VYUY8_2X8, V4L2_COLORSPACE_REC709, 6 }, 337f5fe58fdSBoris BREZILLON { MEDIA_BUS_FMT_RGB565_2X8_BE, V4L2_COLORSPACE_JPEG, 0 }, 3387d459937SAndrzej Hajda }; 3397d459937SAndrzej Hajda 3407d459937SAndrzej Hajda static struct v4l2_rect s5k5baf_cis_rect = { 3417d459937SAndrzej Hajda 0, 0, S5K5BAF_CIS_WIDTH, S5K5BAF_CIS_HEIGHT 3427d459937SAndrzej Hajda }; 3437d459937SAndrzej Hajda 3447d459937SAndrzej Hajda /* Setfile contains set of I2C command sequences. Each sequence has its ID. 3457d459937SAndrzej Hajda * setfile format: 3467d459937SAndrzej Hajda * u8 magic[4]; 3477d459937SAndrzej Hajda * u16 count; number of sequences 3487d459937SAndrzej Hajda * struct { 3497d459937SAndrzej Hajda * u16 id; sequence id 3507d459937SAndrzej Hajda * u16 offset; sequence offset in data array 3517d459937SAndrzej Hajda * } seq[count]; 3527d459937SAndrzej Hajda * u16 data[*]; array containing sequences 3537d459937SAndrzej Hajda * 3547d459937SAndrzej Hajda */ 3557d459937SAndrzej Hajda static int s5k5baf_fw_parse(struct device *dev, struct s5k5baf_fw **fw, 3560348bb1aSHans Verkuil size_t count, const __le16 *data) 3577d459937SAndrzej Hajda { 3587d459937SAndrzej Hajda struct s5k5baf_fw *f; 3597d459937SAndrzej Hajda u16 *d, i, *end; 3607d459937SAndrzej Hajda int ret; 3617d459937SAndrzej Hajda 3627d459937SAndrzej Hajda if (count < S5K5BAG_FW_TAG_LEN + 1) { 363383cc04cSSachin Kamat dev_err(dev, "firmware file too short (%zu)\n", count); 3647d459937SAndrzej Hajda return -EINVAL; 3657d459937SAndrzej Hajda } 3667d459937SAndrzej Hajda 3677d459937SAndrzej Hajda ret = memcmp(data, S5K5BAF_FW_TAG, S5K5BAG_FW_TAG_LEN * sizeof(u16)); 3687d459937SAndrzej Hajda if (ret != 0) { 3697d459937SAndrzej Hajda dev_err(dev, "invalid firmware magic number\n"); 3707d459937SAndrzej Hajda return -EINVAL; 3717d459937SAndrzej Hajda } 3727d459937SAndrzej Hajda 3737d459937SAndrzej Hajda data += S5K5BAG_FW_TAG_LEN; 3747d459937SAndrzej Hajda count -= S5K5BAG_FW_TAG_LEN; 3757d459937SAndrzej Hajda 3767d459937SAndrzej Hajda d = devm_kzalloc(dev, count * sizeof(u16), GFP_KERNEL); 37751b5b3d6SKiran Padwal if (!d) 37851b5b3d6SKiran Padwal return -ENOMEM; 3797d459937SAndrzej Hajda 3807d459937SAndrzej Hajda for (i = 0; i < count; ++i) 3817d459937SAndrzej Hajda d[i] = le16_to_cpu(data[i]); 3827d459937SAndrzej Hajda 3837d459937SAndrzej Hajda f = (struct s5k5baf_fw *)d; 3847d459937SAndrzej Hajda if (count < 1 + 2 * f->count) { 385383cc04cSSachin Kamat dev_err(dev, "invalid firmware header (count=%d size=%zu)\n", 3867d459937SAndrzej Hajda f->count, 2 * (count + S5K5BAG_FW_TAG_LEN)); 3877d459937SAndrzej Hajda return -EINVAL; 3887d459937SAndrzej Hajda } 3897d459937SAndrzej Hajda end = d + count; 3907d459937SAndrzej Hajda d += 1 + 2 * f->count; 3917d459937SAndrzej Hajda 3927d459937SAndrzej Hajda for (i = 0; i < f->count; ++i) { 3937d459937SAndrzej Hajda if (f->seq[i].offset + d <= end) 3947d459937SAndrzej Hajda continue; 3957d459937SAndrzej Hajda dev_err(dev, "invalid firmware header (seq=%d)\n", i); 3967d459937SAndrzej Hajda return -EINVAL; 3977d459937SAndrzej Hajda } 3987d459937SAndrzej Hajda 3997d459937SAndrzej Hajda *fw = f; 4007d459937SAndrzej Hajda 4017d459937SAndrzej Hajda return 0; 4027d459937SAndrzej Hajda } 4037d459937SAndrzej Hajda 4047d459937SAndrzej Hajda static inline struct v4l2_subdev *ctrl_to_sd(struct v4l2_ctrl *ctrl) 4057d459937SAndrzej Hajda { 4067d459937SAndrzej Hajda return &container_of(ctrl->handler, struct s5k5baf, ctrls.handler)->sd; 4077d459937SAndrzej Hajda } 4087d459937SAndrzej Hajda 4097d459937SAndrzej Hajda static inline bool s5k5baf_is_cis_subdev(struct v4l2_subdev *sd) 4107d459937SAndrzej Hajda { 4114ca72efaSMauro Carvalho Chehab return sd->entity.function == MEDIA_ENT_F_CAM_SENSOR; 4127d459937SAndrzej Hajda } 4137d459937SAndrzej Hajda 4147d459937SAndrzej Hajda static inline struct s5k5baf *to_s5k5baf(struct v4l2_subdev *sd) 4157d459937SAndrzej Hajda { 4167d459937SAndrzej Hajda if (s5k5baf_is_cis_subdev(sd)) 4177d459937SAndrzej Hajda return container_of(sd, struct s5k5baf, cis_sd); 4187d459937SAndrzej Hajda else 4197d459937SAndrzej Hajda return container_of(sd, struct s5k5baf, sd); 4207d459937SAndrzej Hajda } 4217d459937SAndrzej Hajda 4227d459937SAndrzej Hajda static u16 s5k5baf_i2c_read(struct s5k5baf *state, u16 addr) 4237d459937SAndrzej Hajda { 4247d459937SAndrzej Hajda struct i2c_client *c = v4l2_get_subdevdata(&state->sd); 4257d459937SAndrzej Hajda __be16 w, r; 4260348bb1aSHans Verkuil u16 res; 4277d459937SAndrzej Hajda struct i2c_msg msg[] = { 4287d459937SAndrzej Hajda { .addr = c->addr, .flags = 0, 4297d459937SAndrzej Hajda .len = 2, .buf = (u8 *)&w }, 4307d459937SAndrzej Hajda { .addr = c->addr, .flags = I2C_M_RD, 4317d459937SAndrzej Hajda .len = 2, .buf = (u8 *)&r }, 4327d459937SAndrzej Hajda }; 4337d459937SAndrzej Hajda int ret; 4347d459937SAndrzej Hajda 4357d459937SAndrzej Hajda if (state->error) 4367d459937SAndrzej Hajda return 0; 4377d459937SAndrzej Hajda 4387d459937SAndrzej Hajda w = cpu_to_be16(addr); 4397d459937SAndrzej Hajda ret = i2c_transfer(c->adapter, msg, 2); 4400348bb1aSHans Verkuil res = be16_to_cpu(r); 4417d459937SAndrzej Hajda 4420348bb1aSHans Verkuil v4l2_dbg(3, debug, c, "i2c_read: 0x%04x : 0x%04x\n", addr, res); 4437d459937SAndrzej Hajda 4447d459937SAndrzej Hajda if (ret != 2) { 4457d459937SAndrzej Hajda v4l2_err(c, "i2c_read: error during transfer (%d)\n", ret); 4467d459937SAndrzej Hajda state->error = ret; 4477d459937SAndrzej Hajda } 4480348bb1aSHans Verkuil return res; 4497d459937SAndrzej Hajda } 4507d459937SAndrzej Hajda 4517d459937SAndrzej Hajda static void s5k5baf_i2c_write(struct s5k5baf *state, u16 addr, u16 val) 4527d459937SAndrzej Hajda { 4537d459937SAndrzej Hajda u8 buf[4] = { addr >> 8, addr & 0xFF, val >> 8, val & 0xFF }; 4547d459937SAndrzej Hajda struct i2c_client *c = v4l2_get_subdevdata(&state->sd); 4557d459937SAndrzej Hajda int ret; 4567d459937SAndrzej Hajda 4577d459937SAndrzej Hajda if (state->error) 4587d459937SAndrzej Hajda return; 4597d459937SAndrzej Hajda 4607d459937SAndrzej Hajda ret = i2c_master_send(c, buf, 4); 4617d459937SAndrzej Hajda v4l2_dbg(3, debug, c, "i2c_write: 0x%04x : 0x%04x\n", addr, val); 4627d459937SAndrzej Hajda 4637d459937SAndrzej Hajda if (ret != 4) { 4647d459937SAndrzej Hajda v4l2_err(c, "i2c_write: error during transfer (%d)\n", ret); 4657d459937SAndrzej Hajda state->error = ret; 4667d459937SAndrzej Hajda } 4677d459937SAndrzej Hajda } 4687d459937SAndrzej Hajda 4697d459937SAndrzej Hajda static u16 s5k5baf_read(struct s5k5baf *state, u16 addr) 4707d459937SAndrzej Hajda { 4717d459937SAndrzej Hajda s5k5baf_i2c_write(state, REG_CMDRD_ADDR, addr); 4727d459937SAndrzej Hajda return s5k5baf_i2c_read(state, REG_CMD_BUF); 4737d459937SAndrzej Hajda } 4747d459937SAndrzej Hajda 4757d459937SAndrzej Hajda static void s5k5baf_write(struct s5k5baf *state, u16 addr, u16 val) 4767d459937SAndrzej Hajda { 4777d459937SAndrzej Hajda s5k5baf_i2c_write(state, REG_CMDWR_ADDR, addr); 4787d459937SAndrzej Hajda s5k5baf_i2c_write(state, REG_CMD_BUF, val); 4797d459937SAndrzej Hajda } 4807d459937SAndrzej Hajda 4817d459937SAndrzej Hajda static void s5k5baf_write_arr_seq(struct s5k5baf *state, u16 addr, 4827d459937SAndrzej Hajda u16 count, const u16 *seq) 4837d459937SAndrzej Hajda { 4847d459937SAndrzej Hajda struct i2c_client *c = v4l2_get_subdevdata(&state->sd); 4857e8f15c5SAndrzej Hajda __be16 buf[65]; 4867d459937SAndrzej Hajda 4877d459937SAndrzej Hajda s5k5baf_i2c_write(state, REG_CMDWR_ADDR, addr); 4887d459937SAndrzej Hajda if (state->error) 4897d459937SAndrzej Hajda return; 4907d459937SAndrzej Hajda 4917d459937SAndrzej Hajda v4l2_dbg(3, debug, c, "i2c_write_seq(count=%d): %*ph\n", count, 4927e8f15c5SAndrzej Hajda min(2 * count, 64), seq); 4937d459937SAndrzej Hajda 494555416f9SVaishali Thakkar buf[0] = cpu_to_be16(REG_CMD_BUF); 4957e8f15c5SAndrzej Hajda 4967e8f15c5SAndrzej Hajda while (count > 0) { 4977e8f15c5SAndrzej Hajda int n = min_t(int, count, ARRAY_SIZE(buf) - 1); 4987e8f15c5SAndrzej Hajda int ret, i; 4997e8f15c5SAndrzej Hajda 5007e8f15c5SAndrzej Hajda for (i = 1; i <= n; ++i) 5017e8f15c5SAndrzej Hajda buf[i] = cpu_to_be16(*seq++); 5027e8f15c5SAndrzej Hajda 5037e8f15c5SAndrzej Hajda i *= 2; 5047e8f15c5SAndrzej Hajda ret = i2c_master_send(c, (char *)buf, i); 5057e8f15c5SAndrzej Hajda if (ret != i) { 5067d459937SAndrzej Hajda v4l2_err(c, "i2c_write_seq: error during transfer (%d)\n", ret); 5077d459937SAndrzej Hajda state->error = ret; 5087e8f15c5SAndrzej Hajda break; 5097e8f15c5SAndrzej Hajda } 5107e8f15c5SAndrzej Hajda 5117e8f15c5SAndrzej Hajda count -= n; 5127d459937SAndrzej Hajda } 5137d459937SAndrzej Hajda } 5147d459937SAndrzej Hajda 5157d459937SAndrzej Hajda #define s5k5baf_write_seq(state, addr, seq...) \ 5167d459937SAndrzej Hajda s5k5baf_write_arr_seq(state, addr, sizeof((char[]){ seq }), \ 5177d459937SAndrzej Hajda (const u16 []){ seq }); 5187d459937SAndrzej Hajda 5197d459937SAndrzej Hajda /* add items count at the beginning of the list */ 5207d459937SAndrzej Hajda #define NSEQ(seq...) sizeof((char[]){ seq }), seq 5217d459937SAndrzej Hajda 5227d459937SAndrzej Hajda /* 5237d459937SAndrzej Hajda * s5k5baf_write_nseq() - Writes sequences of values to sensor memory via i2c 5247d459937SAndrzej Hajda * @nseq: sequence of u16 words in format: 5257d459937SAndrzej Hajda * (N, address, value[1]...value[N-1])*,0 5267d459937SAndrzej Hajda * Ex.: 5277d459937SAndrzej Hajda * u16 seq[] = { NSEQ(0x4000, 1, 1), NSEQ(0x4010, 640, 480), 0 }; 5287d459937SAndrzej Hajda * ret = s5k5baf_write_nseq(c, seq); 5297d459937SAndrzej Hajda */ 5307d459937SAndrzej Hajda static void s5k5baf_write_nseq(struct s5k5baf *state, const u16 *nseq) 5317d459937SAndrzej Hajda { 5327d459937SAndrzej Hajda int count; 5337d459937SAndrzej Hajda 5347d459937SAndrzej Hajda while ((count = *nseq++)) { 5357d459937SAndrzej Hajda u16 addr = *nseq++; 5367d459937SAndrzej Hajda --count; 5377d459937SAndrzej Hajda 5387d459937SAndrzej Hajda s5k5baf_write_arr_seq(state, addr, count, nseq); 5397d459937SAndrzej Hajda nseq += count; 5407d459937SAndrzej Hajda } 5417d459937SAndrzej Hajda } 5427d459937SAndrzej Hajda 5437d459937SAndrzej Hajda static void s5k5baf_synchronize(struct s5k5baf *state, int timeout, u16 addr) 5447d459937SAndrzej Hajda { 5457d459937SAndrzej Hajda unsigned long end = jiffies + msecs_to_jiffies(timeout); 5467d459937SAndrzej Hajda u16 reg; 5477d459937SAndrzej Hajda 5487d459937SAndrzej Hajda s5k5baf_write(state, addr, 1); 5497d459937SAndrzej Hajda do { 5507d459937SAndrzej Hajda reg = s5k5baf_read(state, addr); 5517d459937SAndrzej Hajda if (state->error || !reg) 5527d459937SAndrzej Hajda return; 5537d459937SAndrzej Hajda usleep_range(5000, 10000); 5547d459937SAndrzej Hajda } while (time_is_after_jiffies(end)); 5557d459937SAndrzej Hajda 5567d459937SAndrzej Hajda v4l2_err(&state->sd, "timeout on register synchronize (%#x)\n", addr); 5577d459937SAndrzej Hajda state->error = -ETIMEDOUT; 5587d459937SAndrzej Hajda } 5597d459937SAndrzej Hajda 5607d459937SAndrzej Hajda static u16 *s5k5baf_fw_get_seq(struct s5k5baf *state, u16 seq_id) 5617d459937SAndrzej Hajda { 5627d459937SAndrzej Hajda struct s5k5baf_fw *fw = state->fw; 563c0ee6273SSachin Kamat u16 *data; 5647d459937SAndrzej Hajda int i; 5657d459937SAndrzej Hajda 5667d459937SAndrzej Hajda if (fw == NULL) 5677d459937SAndrzej Hajda return NULL; 5687d459937SAndrzej Hajda 569c0ee6273SSachin Kamat data = fw->data + 2 * fw->count; 570c0ee6273SSachin Kamat 5717d459937SAndrzej Hajda for (i = 0; i < fw->count; ++i) { 5727d459937SAndrzej Hajda if (fw->seq[i].id == seq_id) 5737d459937SAndrzej Hajda return data + fw->seq[i].offset; 5747d459937SAndrzej Hajda } 5757d459937SAndrzej Hajda 5767d459937SAndrzej Hajda return NULL; 5777d459937SAndrzej Hajda } 5787d459937SAndrzej Hajda 5797d459937SAndrzej Hajda static void s5k5baf_hw_patch(struct s5k5baf *state) 5807d459937SAndrzej Hajda { 5817d459937SAndrzej Hajda u16 *seq = s5k5baf_fw_get_seq(state, S5K5BAF_FW_ID_PATCH); 5827d459937SAndrzej Hajda 5837d459937SAndrzej Hajda if (seq) 5847d459937SAndrzej Hajda s5k5baf_write_nseq(state, seq); 5857d459937SAndrzej Hajda } 5867d459937SAndrzej Hajda 5877d459937SAndrzej Hajda static void s5k5baf_hw_set_clocks(struct s5k5baf *state) 5887d459937SAndrzej Hajda { 5897d459937SAndrzej Hajda unsigned long mclk = state->mclk_frequency / 1000; 5907d459937SAndrzej Hajda u16 status; 5917d459937SAndrzej Hajda static const u16 nseq_clk_cfg[] = { 5927d459937SAndrzej Hajda NSEQ(REG_I_USE_NPVI_CLOCKS, 5937d459937SAndrzej Hajda NPVI_CLOCKS, NMIPI_CLOCKS, 0, 5947d459937SAndrzej Hajda SCLK_PVI_FREQ / 4, PCLK_MIN_FREQ / 4, PCLK_MAX_FREQ / 4, 5957d459937SAndrzej Hajda SCLK_MIPI_FREQ / 4, PCLK_MIN_FREQ / 4, PCLK_MAX_FREQ / 4), 5967d459937SAndrzej Hajda NSEQ(REG_I_USE_REGS_API, 1), 5977d459937SAndrzej Hajda 0 5987d459937SAndrzej Hajda }; 5997d459937SAndrzej Hajda 6007d459937SAndrzej Hajda s5k5baf_write_seq(state, REG_I_INCLK_FREQ_L, mclk & 0xffff, mclk >> 16); 6017d459937SAndrzej Hajda s5k5baf_write_nseq(state, nseq_clk_cfg); 6027d459937SAndrzej Hajda 6037d459937SAndrzej Hajda s5k5baf_synchronize(state, 250, REG_I_INIT_PARAMS_UPDATED); 6047d459937SAndrzej Hajda status = s5k5baf_read(state, REG_I_ERROR_INFO); 6057d459937SAndrzej Hajda if (!state->error && status) { 6067d459937SAndrzej Hajda v4l2_err(&state->sd, "error configuring PLL (%d)\n", status); 6077d459937SAndrzej Hajda state->error = -EINVAL; 6087d459937SAndrzej Hajda } 6097d459937SAndrzej Hajda } 6107d459937SAndrzej Hajda 6117d459937SAndrzej Hajda /* set custom color correction matrices for various illuminations */ 6127d459937SAndrzej Hajda static void s5k5baf_hw_set_ccm(struct s5k5baf *state) 6137d459937SAndrzej Hajda { 6147d459937SAndrzej Hajda u16 *seq = s5k5baf_fw_get_seq(state, S5K5BAF_FW_ID_CCM); 6157d459937SAndrzej Hajda 6167d459937SAndrzej Hajda if (seq) 6177d459937SAndrzej Hajda s5k5baf_write_nseq(state, seq); 6187d459937SAndrzej Hajda } 6197d459937SAndrzej Hajda 6207d459937SAndrzej Hajda /* CIS sensor tuning, based on undocumented android driver code */ 6217d459937SAndrzej Hajda static void s5k5baf_hw_set_cis(struct s5k5baf *state) 6227d459937SAndrzej Hajda { 6237d459937SAndrzej Hajda u16 *seq = s5k5baf_fw_get_seq(state, S5K5BAF_FW_ID_CIS); 6247d459937SAndrzej Hajda 6257d459937SAndrzej Hajda if (!seq) 6267d459937SAndrzej Hajda return; 6277d459937SAndrzej Hajda 6287d459937SAndrzej Hajda s5k5baf_i2c_write(state, REG_CMDWR_PAGE, PAGE_IF_HW); 6297d459937SAndrzej Hajda s5k5baf_write_nseq(state, seq); 6307d459937SAndrzej Hajda s5k5baf_i2c_write(state, REG_CMDWR_PAGE, PAGE_IF_SW); 6317d459937SAndrzej Hajda } 6327d459937SAndrzej Hajda 6337d459937SAndrzej Hajda static void s5k5baf_hw_sync_cfg(struct s5k5baf *state) 6347d459937SAndrzej Hajda { 6357d459937SAndrzej Hajda s5k5baf_write(state, REG_G_PREV_CFG_CHG, 1); 6367d459937SAndrzej Hajda if (state->apply_crop) { 6377d459937SAndrzej Hajda s5k5baf_write(state, REG_G_INPUTS_CHANGE_REQ, 1); 6387d459937SAndrzej Hajda s5k5baf_write(state, REG_G_PREV_CFG_BYPASS_CHANGED, 1); 6397d459937SAndrzej Hajda } 6407d459937SAndrzej Hajda s5k5baf_synchronize(state, 500, REG_G_NEW_CFG_SYNC); 6417d459937SAndrzej Hajda } 6427d459937SAndrzej Hajda /* Set horizontal and vertical image flipping */ 6437d459937SAndrzej Hajda static void s5k5baf_hw_set_mirror(struct s5k5baf *state) 6447d459937SAndrzej Hajda { 6457d459937SAndrzej Hajda u16 flip = state->ctrls.vflip->val | (state->ctrls.vflip->val << 1); 6467d459937SAndrzej Hajda 6477d459937SAndrzej Hajda s5k5baf_write(state, REG_P_PREV_MIRROR(0), flip); 6487d459937SAndrzej Hajda if (state->streaming) 6497d459937SAndrzej Hajda s5k5baf_hw_sync_cfg(state); 6507d459937SAndrzej Hajda } 6517d459937SAndrzej Hajda 6527d459937SAndrzej Hajda static void s5k5baf_hw_set_alg(struct s5k5baf *state, u16 alg, bool enable) 6537d459937SAndrzej Hajda { 6547d459937SAndrzej Hajda u16 cur_alg, new_alg; 6557d459937SAndrzej Hajda 6567d459937SAndrzej Hajda if (!state->valid_auto_alg) 6577d459937SAndrzej Hajda cur_alg = s5k5baf_read(state, REG_DBG_AUTOALG_EN); 6587d459937SAndrzej Hajda else 6597d459937SAndrzej Hajda cur_alg = state->auto_alg; 6607d459937SAndrzej Hajda 6617d459937SAndrzej Hajda new_alg = enable ? (cur_alg | alg) : (cur_alg & ~alg); 6627d459937SAndrzej Hajda 6637d459937SAndrzej Hajda if (new_alg != cur_alg) 6647d459937SAndrzej Hajda s5k5baf_write(state, REG_DBG_AUTOALG_EN, new_alg); 6657d459937SAndrzej Hajda 6667d459937SAndrzej Hajda if (state->error) 6677d459937SAndrzej Hajda return; 6687d459937SAndrzej Hajda 6697d459937SAndrzej Hajda state->valid_auto_alg = 1; 6707d459937SAndrzej Hajda state->auto_alg = new_alg; 6717d459937SAndrzej Hajda } 6727d459937SAndrzej Hajda 6737d459937SAndrzej Hajda /* Configure auto/manual white balance and R/G/B gains */ 6747d459937SAndrzej Hajda static void s5k5baf_hw_set_awb(struct s5k5baf *state, int awb) 6757d459937SAndrzej Hajda { 6767d459937SAndrzej Hajda struct s5k5baf_ctrls *ctrls = &state->ctrls; 6777d459937SAndrzej Hajda 6787d459937SAndrzej Hajda if (!awb) 6797d459937SAndrzej Hajda s5k5baf_write_seq(state, REG_SF_RGAIN, 6807d459937SAndrzej Hajda ctrls->gain_red->val, 1, 6817d459937SAndrzej Hajda S5K5BAF_GAIN_GREEN_DEF, 1, 6827d459937SAndrzej Hajda ctrls->gain_blue->val, 1, 6837d459937SAndrzej Hajda 1); 6847d459937SAndrzej Hajda 6857d459937SAndrzej Hajda s5k5baf_hw_set_alg(state, AALG_WB_EN, awb); 6867d459937SAndrzej Hajda } 6877d459937SAndrzej Hajda 6887d459937SAndrzej Hajda /* Program FW with exposure time, 'exposure' in us units */ 6897d459937SAndrzej Hajda static void s5k5baf_hw_set_user_exposure(struct s5k5baf *state, int exposure) 6907d459937SAndrzej Hajda { 6917d459937SAndrzej Hajda unsigned int time = exposure / 10; 6927d459937SAndrzej Hajda 6937d459937SAndrzej Hajda s5k5baf_write_seq(state, REG_SF_USR_EXPOSURE_L, 6947d459937SAndrzej Hajda time & 0xffff, time >> 16, 1); 6957d459937SAndrzej Hajda } 6967d459937SAndrzej Hajda 6977d459937SAndrzej Hajda static void s5k5baf_hw_set_user_gain(struct s5k5baf *state, int gain) 6987d459937SAndrzej Hajda { 6997d459937SAndrzej Hajda s5k5baf_write_seq(state, REG_SF_USR_TOT_GAIN, gain, 1); 7007d459937SAndrzej Hajda } 7017d459937SAndrzej Hajda 7027d459937SAndrzej Hajda /* Set auto/manual exposure and total gain */ 7037d459937SAndrzej Hajda static void s5k5baf_hw_set_auto_exposure(struct s5k5baf *state, int value) 7047d459937SAndrzej Hajda { 7057d459937SAndrzej Hajda if (value == V4L2_EXPOSURE_AUTO) { 7067d459937SAndrzej Hajda s5k5baf_hw_set_alg(state, AALG_AE_EN | AALG_DIVLEI_EN, true); 7077d459937SAndrzej Hajda } else { 7087d459937SAndrzej Hajda unsigned int exp_time = state->ctrls.exposure->val; 7097d459937SAndrzej Hajda 7107d459937SAndrzej Hajda s5k5baf_hw_set_user_exposure(state, exp_time); 7117d459937SAndrzej Hajda s5k5baf_hw_set_user_gain(state, state->ctrls.gain->val); 7127d459937SAndrzej Hajda s5k5baf_hw_set_alg(state, AALG_AE_EN | AALG_DIVLEI_EN, false); 7137d459937SAndrzej Hajda } 7147d459937SAndrzej Hajda } 7157d459937SAndrzej Hajda 7167d459937SAndrzej Hajda static void s5k5baf_hw_set_anti_flicker(struct s5k5baf *state, int v) 7177d459937SAndrzej Hajda { 7187d459937SAndrzej Hajda if (v == V4L2_CID_POWER_LINE_FREQUENCY_AUTO) { 7197d459937SAndrzej Hajda s5k5baf_hw_set_alg(state, AALG_FLICKER_EN, true); 7207d459937SAndrzej Hajda } else { 7217d459937SAndrzej Hajda /* The V4L2_CID_LINE_FREQUENCY control values match 7227d459937SAndrzej Hajda * the register values */ 7237d459937SAndrzej Hajda s5k5baf_write_seq(state, REG_SF_FLICKER_QUANT, v, 1); 7247d459937SAndrzej Hajda s5k5baf_hw_set_alg(state, AALG_FLICKER_EN, false); 7257d459937SAndrzej Hajda } 7267d459937SAndrzej Hajda } 7277d459937SAndrzej Hajda 7287d459937SAndrzej Hajda static void s5k5baf_hw_set_colorfx(struct s5k5baf *state, int val) 7297d459937SAndrzej Hajda { 7307d459937SAndrzej Hajda static const u16 colorfx[] = { 7317d459937SAndrzej Hajda [V4L2_COLORFX_NONE] = 0, 7327d459937SAndrzej Hajda [V4L2_COLORFX_BW] = 1, 7337d459937SAndrzej Hajda [V4L2_COLORFX_NEGATIVE] = 2, 7347d459937SAndrzej Hajda [V4L2_COLORFX_SEPIA] = 3, 7357d459937SAndrzej Hajda [V4L2_COLORFX_SKY_BLUE] = 4, 7367d459937SAndrzej Hajda [V4L2_COLORFX_SKETCH] = 5, 7377d459937SAndrzej Hajda }; 7387d459937SAndrzej Hajda 7397d459937SAndrzej Hajda s5k5baf_write(state, REG_G_SPEC_EFFECTS, colorfx[val]); 7407d459937SAndrzej Hajda } 7417d459937SAndrzej Hajda 7427d459937SAndrzej Hajda static int s5k5baf_find_pixfmt(struct v4l2_mbus_framefmt *mf) 7437d459937SAndrzej Hajda { 7447d459937SAndrzej Hajda int i, c = -1; 7457d459937SAndrzej Hajda 7467d459937SAndrzej Hajda for (i = 0; i < ARRAY_SIZE(s5k5baf_formats); i++) { 7477d459937SAndrzej Hajda if (mf->colorspace != s5k5baf_formats[i].colorspace) 7487d459937SAndrzej Hajda continue; 7497d459937SAndrzej Hajda if (mf->code == s5k5baf_formats[i].code) 7507d459937SAndrzej Hajda return i; 7517d459937SAndrzej Hajda if (c < 0) 7527d459937SAndrzej Hajda c = i; 7537d459937SAndrzej Hajda } 7547d459937SAndrzej Hajda return (c < 0) ? 0 : c; 7557d459937SAndrzej Hajda } 7567d459937SAndrzej Hajda 7577d459937SAndrzej Hajda static int s5k5baf_clear_error(struct s5k5baf *state) 7587d459937SAndrzej Hajda { 7597d459937SAndrzej Hajda int ret = state->error; 7607d459937SAndrzej Hajda 7617d459937SAndrzej Hajda state->error = 0; 7627d459937SAndrzej Hajda return ret; 7637d459937SAndrzej Hajda } 7647d459937SAndrzej Hajda 7657d459937SAndrzej Hajda static int s5k5baf_hw_set_video_bus(struct s5k5baf *state) 7667d459937SAndrzej Hajda { 7677d459937SAndrzej Hajda u16 en_pkts; 7687d459937SAndrzej Hajda 7697d459937SAndrzej Hajda if (state->bus_type == V4L2_MBUS_CSI2) 7707d459937SAndrzej Hajda en_pkts = EN_PACKETS_CSI2; 7717d459937SAndrzej Hajda else 7727d459937SAndrzej Hajda en_pkts = 0; 7737d459937SAndrzej Hajda 7747d459937SAndrzej Hajda s5k5baf_write_seq(state, REG_OIF_EN_MIPI_LANES, 7757d459937SAndrzej Hajda state->nlanes, en_pkts, 1); 7767d459937SAndrzej Hajda 7777d459937SAndrzej Hajda return s5k5baf_clear_error(state); 7787d459937SAndrzej Hajda } 7797d459937SAndrzej Hajda 7807d459937SAndrzej Hajda static u16 s5k5baf_get_cfg_error(struct s5k5baf *state) 7817d459937SAndrzej Hajda { 7827d459937SAndrzej Hajda u16 err = s5k5baf_read(state, REG_G_PREV_CFG_ERROR); 7837d459937SAndrzej Hajda if (err) 7847d459937SAndrzej Hajda s5k5baf_write(state, REG_G_PREV_CFG_ERROR, 0); 7857d459937SAndrzej Hajda return err; 7867d459937SAndrzej Hajda } 7877d459937SAndrzej Hajda 7887d459937SAndrzej Hajda static void s5k5baf_hw_set_fiv(struct s5k5baf *state, u16 fiv) 7897d459937SAndrzej Hajda { 7907d459937SAndrzej Hajda s5k5baf_write(state, REG_P_MAX_FR_TIME(0), fiv); 7917d459937SAndrzej Hajda s5k5baf_hw_sync_cfg(state); 7927d459937SAndrzej Hajda } 7937d459937SAndrzej Hajda 7947d459937SAndrzej Hajda static void s5k5baf_hw_find_min_fiv(struct s5k5baf *state) 7957d459937SAndrzej Hajda { 7967d459937SAndrzej Hajda u16 err, fiv; 7977d459937SAndrzej Hajda int n; 7987d459937SAndrzej Hajda 7997d459937SAndrzej Hajda fiv = s5k5baf_read(state, REG_G_ACTUAL_P_FR_TIME); 8007d459937SAndrzej Hajda if (state->error) 8017d459937SAndrzej Hajda return; 8027d459937SAndrzej Hajda 8037d459937SAndrzej Hajda for (n = 5; n > 0; --n) { 8047d459937SAndrzej Hajda s5k5baf_hw_set_fiv(state, fiv); 8057d459937SAndrzej Hajda err = s5k5baf_get_cfg_error(state); 8067d459937SAndrzej Hajda if (state->error) 8077d459937SAndrzej Hajda return; 8087d459937SAndrzej Hajda switch (err) { 8097d459937SAndrzej Hajda case CFG_ERROR_RANGE: 8107d459937SAndrzej Hajda ++fiv; 8117d459937SAndrzej Hajda break; 8127d459937SAndrzej Hajda case 0: 8137d459937SAndrzej Hajda state->fiv = fiv; 8147d459937SAndrzej Hajda v4l2_info(&state->sd, 8157d459937SAndrzej Hajda "found valid frame interval: %d00us\n", fiv); 8167d459937SAndrzej Hajda return; 8177d459937SAndrzej Hajda default: 8187d459937SAndrzej Hajda v4l2_err(&state->sd, 8197d459937SAndrzej Hajda "error setting frame interval: %d\n", err); 8207d459937SAndrzej Hajda state->error = -EINVAL; 8217d459937SAndrzej Hajda } 822bc39d69aSMauro Carvalho Chehab } 8237d459937SAndrzej Hajda v4l2_err(&state->sd, "cannot find correct frame interval\n"); 8247d459937SAndrzej Hajda state->error = -ERANGE; 8257d459937SAndrzej Hajda } 8267d459937SAndrzej Hajda 8277d459937SAndrzej Hajda static void s5k5baf_hw_validate_cfg(struct s5k5baf *state) 8287d459937SAndrzej Hajda { 8297d459937SAndrzej Hajda u16 err; 8307d459937SAndrzej Hajda 8317d459937SAndrzej Hajda err = s5k5baf_get_cfg_error(state); 8327d459937SAndrzej Hajda if (state->error) 8337d459937SAndrzej Hajda return; 8347d459937SAndrzej Hajda 8357d459937SAndrzej Hajda switch (err) { 8367d459937SAndrzej Hajda case 0: 8377d459937SAndrzej Hajda state->apply_cfg = 1; 8387d459937SAndrzej Hajda return; 8397d459937SAndrzej Hajda case CFG_ERROR_RANGE: 8407d459937SAndrzej Hajda s5k5baf_hw_find_min_fiv(state); 8417d459937SAndrzej Hajda if (!state->error) 8427d459937SAndrzej Hajda state->apply_cfg = 1; 8437d459937SAndrzej Hajda return; 8447d459937SAndrzej Hajda default: 8457d459937SAndrzej Hajda v4l2_err(&state->sd, 8467d459937SAndrzej Hajda "error setting format: %d\n", err); 8477d459937SAndrzej Hajda state->error = -EINVAL; 8487d459937SAndrzej Hajda } 8497d459937SAndrzej Hajda } 8507d459937SAndrzej Hajda 8517d459937SAndrzej Hajda static void s5k5baf_rescale(struct v4l2_rect *r, const struct v4l2_rect *v, 8527d459937SAndrzej Hajda const struct v4l2_rect *n, 8537d459937SAndrzej Hajda const struct v4l2_rect *d) 8547d459937SAndrzej Hajda { 8557d459937SAndrzej Hajda r->left = v->left * n->width / d->width; 8567d459937SAndrzej Hajda r->top = v->top * n->height / d->height; 8577d459937SAndrzej Hajda r->width = v->width * n->width / d->width; 8587d459937SAndrzej Hajda r->height = v->height * n->height / d->height; 8597d459937SAndrzej Hajda } 8607d459937SAndrzej Hajda 8617d459937SAndrzej Hajda static int s5k5baf_hw_set_crop_rects(struct s5k5baf *state) 8627d459937SAndrzej Hajda { 8637d459937SAndrzej Hajda struct v4l2_rect *p, r; 8647d459937SAndrzej Hajda u16 err; 8657d459937SAndrzej Hajda int ret; 8667d459937SAndrzej Hajda 8677d459937SAndrzej Hajda p = &state->crop_sink; 8687d459937SAndrzej Hajda s5k5baf_write_seq(state, REG_G_PREVREQ_IN_WIDTH, p->width, p->height, 8697d459937SAndrzej Hajda p->left, p->top); 8707d459937SAndrzej Hajda 8717d459937SAndrzej Hajda s5k5baf_rescale(&r, &state->crop_source, &state->crop_sink, 8727d459937SAndrzej Hajda &state->compose); 8737d459937SAndrzej Hajda s5k5baf_write_seq(state, REG_G_PREVZOOM_IN_WIDTH, r.width, r.height, 8747d459937SAndrzej Hajda r.left, r.top); 8757d459937SAndrzej Hajda 8767d459937SAndrzej Hajda s5k5baf_synchronize(state, 500, REG_G_INPUTS_CHANGE_REQ); 8777d459937SAndrzej Hajda s5k5baf_synchronize(state, 500, REG_G_PREV_CFG_BYPASS_CHANGED); 8787d459937SAndrzej Hajda err = s5k5baf_get_cfg_error(state); 8797d459937SAndrzej Hajda ret = s5k5baf_clear_error(state); 8807d459937SAndrzej Hajda if (ret < 0) 8817d459937SAndrzej Hajda return ret; 8827d459937SAndrzej Hajda 8837d459937SAndrzej Hajda switch (err) { 8847d459937SAndrzej Hajda case 0: 8857d459937SAndrzej Hajda break; 8867d459937SAndrzej Hajda case CFG_ERROR_RANGE: 8877d459937SAndrzej Hajda /* retry crop with frame interval set to max */ 8887d459937SAndrzej Hajda s5k5baf_hw_set_fiv(state, S5K5BAF_MAX_FR_TIME); 8897d459937SAndrzej Hajda err = s5k5baf_get_cfg_error(state); 8907d459937SAndrzej Hajda ret = s5k5baf_clear_error(state); 8917d459937SAndrzej Hajda if (ret < 0) 8927d459937SAndrzej Hajda return ret; 8937d459937SAndrzej Hajda if (err) { 8947d459937SAndrzej Hajda v4l2_err(&state->sd, 8957d459937SAndrzej Hajda "crop error on max frame interval: %d\n", err); 8967d459937SAndrzej Hajda state->error = -EINVAL; 8977d459937SAndrzej Hajda } 8987d459937SAndrzej Hajda s5k5baf_hw_set_fiv(state, state->req_fiv); 8997d459937SAndrzej Hajda s5k5baf_hw_validate_cfg(state); 9007d459937SAndrzej Hajda break; 9017d459937SAndrzej Hajda default: 9027d459937SAndrzej Hajda v4l2_err(&state->sd, "crop error: %d\n", err); 9037d459937SAndrzej Hajda return -EINVAL; 9047d459937SAndrzej Hajda } 9057d459937SAndrzej Hajda 9067d459937SAndrzej Hajda if (!state->apply_cfg) 9077d459937SAndrzej Hajda return 0; 9087d459937SAndrzej Hajda 9097d459937SAndrzej Hajda p = &state->crop_source; 9107d459937SAndrzej Hajda s5k5baf_write_seq(state, REG_P_OUT_WIDTH(0), p->width, p->height); 9117d459937SAndrzej Hajda s5k5baf_hw_set_fiv(state, state->req_fiv); 9127d459937SAndrzej Hajda s5k5baf_hw_validate_cfg(state); 9137d459937SAndrzej Hajda 9147d459937SAndrzej Hajda return s5k5baf_clear_error(state); 9157d459937SAndrzej Hajda } 9167d459937SAndrzej Hajda 9177d459937SAndrzej Hajda static void s5k5baf_hw_set_config(struct s5k5baf *state) 9187d459937SAndrzej Hajda { 9197d459937SAndrzej Hajda u16 reg_fmt = s5k5baf_formats[state->pixfmt].reg_p_fmt; 9207d459937SAndrzej Hajda struct v4l2_rect *r = &state->crop_source; 9217d459937SAndrzej Hajda 9227d459937SAndrzej Hajda s5k5baf_write_seq(state, REG_P_OUT_WIDTH(0), 9237d459937SAndrzej Hajda r->width, r->height, reg_fmt, 9247d459937SAndrzej Hajda PCLK_MAX_FREQ >> 2, PCLK_MIN_FREQ >> 2, 9257d459937SAndrzej Hajda PVI_MASK_MIPI, CLK_MIPI_INDEX, 9267d459937SAndrzej Hajda FR_RATE_FIXED, FR_RATE_Q_DYNAMIC, 9277d459937SAndrzej Hajda state->req_fiv, S5K5BAF_MIN_FR_TIME); 9287d459937SAndrzej Hajda s5k5baf_hw_sync_cfg(state); 9297d459937SAndrzej Hajda s5k5baf_hw_validate_cfg(state); 9307d459937SAndrzej Hajda } 9317d459937SAndrzej Hajda 9327d459937SAndrzej Hajda 9337d459937SAndrzej Hajda static void s5k5baf_hw_set_test_pattern(struct s5k5baf *state, int id) 9347d459937SAndrzej Hajda { 9357d459937SAndrzej Hajda s5k5baf_i2c_write(state, REG_PATTERN_WIDTH, 800); 9367d459937SAndrzej Hajda s5k5baf_i2c_write(state, REG_PATTERN_HEIGHT, 511); 9377d459937SAndrzej Hajda s5k5baf_i2c_write(state, REG_PATTERN_PARAM, 0); 9387d459937SAndrzej Hajda s5k5baf_i2c_write(state, REG_PATTERN_SET, id); 9397d459937SAndrzej Hajda } 9407d459937SAndrzej Hajda 9417d459937SAndrzej Hajda static void s5k5baf_gpio_assert(struct s5k5baf *state, int id) 9427d459937SAndrzej Hajda { 9437d459937SAndrzej Hajda struct s5k5baf_gpio *gpio = &state->gpios[id]; 9447d459937SAndrzej Hajda 9457d459937SAndrzej Hajda gpio_set_value(gpio->gpio, gpio->level); 9467d459937SAndrzej Hajda } 9477d459937SAndrzej Hajda 9487d459937SAndrzej Hajda static void s5k5baf_gpio_deassert(struct s5k5baf *state, int id) 9497d459937SAndrzej Hajda { 9507d459937SAndrzej Hajda struct s5k5baf_gpio *gpio = &state->gpios[id]; 9517d459937SAndrzej Hajda 9527d459937SAndrzej Hajda gpio_set_value(gpio->gpio, !gpio->level); 9537d459937SAndrzej Hajda } 9547d459937SAndrzej Hajda 9557d459937SAndrzej Hajda static int s5k5baf_power_on(struct s5k5baf *state) 9567d459937SAndrzej Hajda { 9577d459937SAndrzej Hajda int ret; 9587d459937SAndrzej Hajda 9597d459937SAndrzej Hajda ret = regulator_bulk_enable(S5K5BAF_NUM_SUPPLIES, state->supplies); 9607d459937SAndrzej Hajda if (ret < 0) 9617d459937SAndrzej Hajda goto err; 9627d459937SAndrzej Hajda 9637d459937SAndrzej Hajda ret = clk_set_rate(state->clock, state->mclk_frequency); 9647d459937SAndrzej Hajda if (ret < 0) 9657d459937SAndrzej Hajda goto err_reg_dis; 9667d459937SAndrzej Hajda 9677d459937SAndrzej Hajda ret = clk_prepare_enable(state->clock); 9687d459937SAndrzej Hajda if (ret < 0) 9697d459937SAndrzej Hajda goto err_reg_dis; 9707d459937SAndrzej Hajda 9717d459937SAndrzej Hajda v4l2_dbg(1, debug, &state->sd, "clock frequency: %ld\n", 9727d459937SAndrzej Hajda clk_get_rate(state->clock)); 9737d459937SAndrzej Hajda 9747d459937SAndrzej Hajda s5k5baf_gpio_deassert(state, STBY); 9757d459937SAndrzej Hajda usleep_range(50, 100); 9767d459937SAndrzej Hajda s5k5baf_gpio_deassert(state, RST); 9777d459937SAndrzej Hajda return 0; 9787d459937SAndrzej Hajda 9797d459937SAndrzej Hajda err_reg_dis: 9807d459937SAndrzej Hajda regulator_bulk_disable(S5K5BAF_NUM_SUPPLIES, state->supplies); 9817d459937SAndrzej Hajda err: 9827d459937SAndrzej Hajda v4l2_err(&state->sd, "%s() failed (%d)\n", __func__, ret); 9837d459937SAndrzej Hajda return ret; 9847d459937SAndrzej Hajda } 9857d459937SAndrzej Hajda 9867d459937SAndrzej Hajda static int s5k5baf_power_off(struct s5k5baf *state) 9877d459937SAndrzej Hajda { 9887d459937SAndrzej Hajda int ret; 9897d459937SAndrzej Hajda 9907d459937SAndrzej Hajda state->streaming = 0; 9917d459937SAndrzej Hajda state->apply_cfg = 0; 9927d459937SAndrzej Hajda state->apply_crop = 0; 9937d459937SAndrzej Hajda 9947d459937SAndrzej Hajda s5k5baf_gpio_assert(state, RST); 9957d459937SAndrzej Hajda s5k5baf_gpio_assert(state, STBY); 9967d459937SAndrzej Hajda 9977d459937SAndrzej Hajda if (!IS_ERR(state->clock)) 9987d459937SAndrzej Hajda clk_disable_unprepare(state->clock); 9997d459937SAndrzej Hajda 10007d459937SAndrzej Hajda ret = regulator_bulk_disable(S5K5BAF_NUM_SUPPLIES, 10017d459937SAndrzej Hajda state->supplies); 10027d459937SAndrzej Hajda if (ret < 0) 10037d459937SAndrzej Hajda v4l2_err(&state->sd, "failed to disable regulators\n"); 10047d459937SAndrzej Hajda 10057d459937SAndrzej Hajda return 0; 10067d459937SAndrzej Hajda } 10077d459937SAndrzej Hajda 10087d459937SAndrzej Hajda static void s5k5baf_hw_init(struct s5k5baf *state) 10097d459937SAndrzej Hajda { 10107d459937SAndrzej Hajda s5k5baf_i2c_write(state, AHB_MSB_ADDR_PTR, PAGE_IF_HW); 10117d459937SAndrzej Hajda s5k5baf_i2c_write(state, REG_CLEAR_HOST_INT, 0); 10127d459937SAndrzej Hajda s5k5baf_i2c_write(state, REG_SW_LOAD_COMPLETE, 1); 10137d459937SAndrzej Hajda s5k5baf_i2c_write(state, REG_CMDRD_PAGE, PAGE_IF_SW); 10147d459937SAndrzej Hajda s5k5baf_i2c_write(state, REG_CMDWR_PAGE, PAGE_IF_SW); 10157d459937SAndrzej Hajda } 10167d459937SAndrzej Hajda 10177d459937SAndrzej Hajda /* 10187d459937SAndrzej Hajda * V4L2 subdev core and video operations 10197d459937SAndrzej Hajda */ 10207d459937SAndrzej Hajda 10217d459937SAndrzej Hajda static void s5k5baf_initialize_data(struct s5k5baf *state) 10227d459937SAndrzej Hajda { 10237d459937SAndrzej Hajda state->pixfmt = 0; 10247d459937SAndrzej Hajda state->req_fiv = 10000 / 15; 10257d459937SAndrzej Hajda state->fiv = state->req_fiv; 10267d459937SAndrzej Hajda state->valid_auto_alg = 0; 10277d459937SAndrzej Hajda } 10287d459937SAndrzej Hajda 10297d459937SAndrzej Hajda static int s5k5baf_load_setfile(struct s5k5baf *state) 10307d459937SAndrzej Hajda { 10317d459937SAndrzej Hajda struct i2c_client *c = v4l2_get_subdevdata(&state->sd); 10327d459937SAndrzej Hajda const struct firmware *fw; 10337d459937SAndrzej Hajda int ret; 10347d459937SAndrzej Hajda 10357d459937SAndrzej Hajda ret = request_firmware(&fw, S5K5BAF_FW_FILENAME, &c->dev); 10367d459937SAndrzej Hajda if (ret < 0) { 10377d459937SAndrzej Hajda dev_warn(&c->dev, "firmware file (%s) not loaded\n", 10387d459937SAndrzej Hajda S5K5BAF_FW_FILENAME); 10397d459937SAndrzej Hajda return ret; 10407d459937SAndrzej Hajda } 10417d459937SAndrzej Hajda 10427d459937SAndrzej Hajda ret = s5k5baf_fw_parse(&c->dev, &state->fw, fw->size / 2, 10430348bb1aSHans Verkuil (__le16 *)fw->data); 10447d459937SAndrzej Hajda 10457d459937SAndrzej Hajda release_firmware(fw); 10467d459937SAndrzej Hajda 10477d459937SAndrzej Hajda return ret; 10487d459937SAndrzej Hajda } 10497d459937SAndrzej Hajda 10507d459937SAndrzej Hajda static int s5k5baf_set_power(struct v4l2_subdev *sd, int on) 10517d459937SAndrzej Hajda { 10527d459937SAndrzej Hajda struct s5k5baf *state = to_s5k5baf(sd); 10537d459937SAndrzej Hajda int ret = 0; 10547d459937SAndrzej Hajda 10557d459937SAndrzej Hajda mutex_lock(&state->lock); 10567d459937SAndrzej Hajda 1057874c65f0SHans Verkuil if (state->power != !on) 10587d459937SAndrzej Hajda goto out; 10597d459937SAndrzej Hajda 10607d459937SAndrzej Hajda if (on) { 10617d459937SAndrzej Hajda if (state->fw == NULL) 10627d459937SAndrzej Hajda s5k5baf_load_setfile(state); 10637d459937SAndrzej Hajda 10647d459937SAndrzej Hajda s5k5baf_initialize_data(state); 10657d459937SAndrzej Hajda ret = s5k5baf_power_on(state); 10667d459937SAndrzej Hajda if (ret < 0) 10677d459937SAndrzej Hajda goto out; 10687d459937SAndrzej Hajda 10697d459937SAndrzej Hajda s5k5baf_hw_init(state); 10707d459937SAndrzej Hajda s5k5baf_hw_patch(state); 10717d459937SAndrzej Hajda s5k5baf_i2c_write(state, REG_SET_HOST_INT, 1); 10727d459937SAndrzej Hajda s5k5baf_hw_set_clocks(state); 10737d459937SAndrzej Hajda 10747d459937SAndrzej Hajda ret = s5k5baf_hw_set_video_bus(state); 10757d459937SAndrzej Hajda if (ret < 0) 10767d459937SAndrzej Hajda goto out; 10777d459937SAndrzej Hajda 10787d459937SAndrzej Hajda s5k5baf_hw_set_cis(state); 10797d459937SAndrzej Hajda s5k5baf_hw_set_ccm(state); 10807d459937SAndrzej Hajda 10817d459937SAndrzej Hajda ret = s5k5baf_clear_error(state); 10827d459937SAndrzej Hajda if (!ret) 10837d459937SAndrzej Hajda state->power++; 10847d459937SAndrzej Hajda } else { 10857d459937SAndrzej Hajda s5k5baf_power_off(state); 10867d459937SAndrzej Hajda state->power--; 10877d459937SAndrzej Hajda } 10887d459937SAndrzej Hajda 10897d459937SAndrzej Hajda out: 10907d459937SAndrzej Hajda mutex_unlock(&state->lock); 10917d459937SAndrzej Hajda 10927d459937SAndrzej Hajda if (!ret && on) 10937d459937SAndrzej Hajda ret = v4l2_ctrl_handler_setup(&state->ctrls.handler); 10947d459937SAndrzej Hajda 10957d459937SAndrzej Hajda return ret; 10967d459937SAndrzej Hajda } 10977d459937SAndrzej Hajda 10987d459937SAndrzej Hajda static void s5k5baf_hw_set_stream(struct s5k5baf *state, int enable) 10997d459937SAndrzej Hajda { 11007d459937SAndrzej Hajda s5k5baf_write_seq(state, REG_G_ENABLE_PREV, enable, 1); 11017d459937SAndrzej Hajda } 11027d459937SAndrzej Hajda 11037d459937SAndrzej Hajda static int s5k5baf_s_stream(struct v4l2_subdev *sd, int on) 11047d459937SAndrzej Hajda { 11057d459937SAndrzej Hajda struct s5k5baf *state = to_s5k5baf(sd); 11067d459937SAndrzej Hajda int ret; 11077d459937SAndrzej Hajda 11087d459937SAndrzej Hajda mutex_lock(&state->lock); 11097d459937SAndrzej Hajda 11107d459937SAndrzej Hajda if (state->streaming == !!on) { 11117d459937SAndrzej Hajda ret = 0; 11127d459937SAndrzej Hajda goto out; 11137d459937SAndrzej Hajda } 11147d459937SAndrzej Hajda 11157d459937SAndrzej Hajda if (on) { 11167d459937SAndrzej Hajda s5k5baf_hw_set_config(state); 11177d459937SAndrzej Hajda ret = s5k5baf_hw_set_crop_rects(state); 11187d459937SAndrzej Hajda if (ret < 0) 11197d459937SAndrzej Hajda goto out; 11207d459937SAndrzej Hajda s5k5baf_hw_set_stream(state, 1); 11217d459937SAndrzej Hajda s5k5baf_i2c_write(state, 0xb0cc, 0x000b); 11227d459937SAndrzej Hajda } else { 11237d459937SAndrzej Hajda s5k5baf_hw_set_stream(state, 0); 11247d459937SAndrzej Hajda } 11257d459937SAndrzej Hajda ret = s5k5baf_clear_error(state); 11267d459937SAndrzej Hajda if (!ret) 11277d459937SAndrzej Hajda state->streaming = !state->streaming; 11287d459937SAndrzej Hajda 11297d459937SAndrzej Hajda out: 11307d459937SAndrzej Hajda mutex_unlock(&state->lock); 11317d459937SAndrzej Hajda 11327d459937SAndrzej Hajda return ret; 11337d459937SAndrzej Hajda } 11347d459937SAndrzej Hajda 11357d459937SAndrzej Hajda static int s5k5baf_g_frame_interval(struct v4l2_subdev *sd, 11367d459937SAndrzej Hajda struct v4l2_subdev_frame_interval *fi) 11377d459937SAndrzej Hajda { 11387d459937SAndrzej Hajda struct s5k5baf *state = to_s5k5baf(sd); 11397d459937SAndrzej Hajda 11407d459937SAndrzej Hajda mutex_lock(&state->lock); 11417d459937SAndrzej Hajda fi->interval.numerator = state->fiv; 11427d459937SAndrzej Hajda fi->interval.denominator = 10000; 11437d459937SAndrzej Hajda mutex_unlock(&state->lock); 11447d459937SAndrzej Hajda 11457d459937SAndrzej Hajda return 0; 11467d459937SAndrzej Hajda } 11477d459937SAndrzej Hajda 11487d459937SAndrzej Hajda static void s5k5baf_set_frame_interval(struct s5k5baf *state, 11497d459937SAndrzej Hajda struct v4l2_subdev_frame_interval *fi) 11507d459937SAndrzej Hajda { 11517d459937SAndrzej Hajda struct v4l2_fract *i = &fi->interval; 11527d459937SAndrzej Hajda 11537d459937SAndrzej Hajda if (fi->interval.denominator == 0) 11547d459937SAndrzej Hajda state->req_fiv = S5K5BAF_MAX_FR_TIME; 11557d459937SAndrzej Hajda else 11567d459937SAndrzej Hajda state->req_fiv = clamp_t(u32, 11577d459937SAndrzej Hajda i->numerator * 10000 / i->denominator, 11587d459937SAndrzej Hajda S5K5BAF_MIN_FR_TIME, 11597d459937SAndrzej Hajda S5K5BAF_MAX_FR_TIME); 11607d459937SAndrzej Hajda 11617d459937SAndrzej Hajda state->fiv = state->req_fiv; 11627d459937SAndrzej Hajda if (state->apply_cfg) { 11637d459937SAndrzej Hajda s5k5baf_hw_set_fiv(state, state->req_fiv); 11647d459937SAndrzej Hajda s5k5baf_hw_validate_cfg(state); 11657d459937SAndrzej Hajda } 11667d459937SAndrzej Hajda *i = (struct v4l2_fract){ state->fiv, 10000 }; 11677d459937SAndrzej Hajda if (state->fiv == state->req_fiv) 11687d459937SAndrzej Hajda v4l2_info(&state->sd, "frame interval changed to %d00us\n", 11697d459937SAndrzej Hajda state->fiv); 11707d459937SAndrzej Hajda } 11717d459937SAndrzej Hajda 11727d459937SAndrzej Hajda static int s5k5baf_s_frame_interval(struct v4l2_subdev *sd, 11737d459937SAndrzej Hajda struct v4l2_subdev_frame_interval *fi) 11747d459937SAndrzej Hajda { 11757d459937SAndrzej Hajda struct s5k5baf *state = to_s5k5baf(sd); 11767d459937SAndrzej Hajda 11777d459937SAndrzej Hajda mutex_lock(&state->lock); 11787d459937SAndrzej Hajda s5k5baf_set_frame_interval(state, fi); 11797d459937SAndrzej Hajda mutex_unlock(&state->lock); 11807d459937SAndrzej Hajda return 0; 11817d459937SAndrzej Hajda } 11827d459937SAndrzej Hajda 11837d459937SAndrzej Hajda /* 11847d459937SAndrzej Hajda * V4L2 subdev pad level and video operations 11857d459937SAndrzej Hajda */ 11867d459937SAndrzej Hajda static int s5k5baf_enum_frame_interval(struct v4l2_subdev *sd, 1187f7234138SHans Verkuil struct v4l2_subdev_pad_config *cfg, 11887d459937SAndrzej Hajda struct v4l2_subdev_frame_interval_enum *fie) 11897d459937SAndrzej Hajda { 11907d459937SAndrzej Hajda if (fie->index > S5K5BAF_MAX_FR_TIME - S5K5BAF_MIN_FR_TIME || 11917d459937SAndrzej Hajda fie->pad != PAD_CIS) 11927d459937SAndrzej Hajda return -EINVAL; 11937d459937SAndrzej Hajda 11947d459937SAndrzej Hajda v4l_bound_align_image(&fie->width, S5K5BAF_WIN_WIDTH_MIN, 11957d459937SAndrzej Hajda S5K5BAF_CIS_WIDTH, 1, 11967d459937SAndrzej Hajda &fie->height, S5K5BAF_WIN_HEIGHT_MIN, 11977d459937SAndrzej Hajda S5K5BAF_CIS_HEIGHT, 1, 0); 11987d459937SAndrzej Hajda 11997d459937SAndrzej Hajda fie->interval.numerator = S5K5BAF_MIN_FR_TIME + fie->index; 12007d459937SAndrzej Hajda fie->interval.denominator = 10000; 12017d459937SAndrzej Hajda 12027d459937SAndrzej Hajda return 0; 12037d459937SAndrzej Hajda } 12047d459937SAndrzej Hajda 12057d459937SAndrzej Hajda static int s5k5baf_enum_mbus_code(struct v4l2_subdev *sd, 1206f7234138SHans Verkuil struct v4l2_subdev_pad_config *cfg, 12077d459937SAndrzej Hajda struct v4l2_subdev_mbus_code_enum *code) 12087d459937SAndrzej Hajda { 12097d459937SAndrzej Hajda if (code->pad == PAD_CIS) { 12107d459937SAndrzej Hajda if (code->index > 0) 12117d459937SAndrzej Hajda return -EINVAL; 1212f5fe58fdSBoris BREZILLON code->code = MEDIA_BUS_FMT_FIXED; 12137d459937SAndrzej Hajda return 0; 12147d459937SAndrzej Hajda } 12157d459937SAndrzej Hajda 12167d459937SAndrzej Hajda if (code->index >= ARRAY_SIZE(s5k5baf_formats)) 12177d459937SAndrzej Hajda return -EINVAL; 12187d459937SAndrzej Hajda 12197d459937SAndrzej Hajda code->code = s5k5baf_formats[code->index].code; 12207d459937SAndrzej Hajda return 0; 12217d459937SAndrzej Hajda } 12227d459937SAndrzej Hajda 12237d459937SAndrzej Hajda static int s5k5baf_enum_frame_size(struct v4l2_subdev *sd, 1224f7234138SHans Verkuil struct v4l2_subdev_pad_config *cfg, 12257d459937SAndrzej Hajda struct v4l2_subdev_frame_size_enum *fse) 12267d459937SAndrzej Hajda { 12277d459937SAndrzej Hajda int i; 12287d459937SAndrzej Hajda 12297d459937SAndrzej Hajda if (fse->index > 0) 12307d459937SAndrzej Hajda return -EINVAL; 12317d459937SAndrzej Hajda 12327d459937SAndrzej Hajda if (fse->pad == PAD_CIS) { 1233f5fe58fdSBoris BREZILLON fse->code = MEDIA_BUS_FMT_FIXED; 12347d459937SAndrzej Hajda fse->min_width = S5K5BAF_CIS_WIDTH; 12357d459937SAndrzej Hajda fse->max_width = S5K5BAF_CIS_WIDTH; 12367d459937SAndrzej Hajda fse->min_height = S5K5BAF_CIS_HEIGHT; 12377d459937SAndrzej Hajda fse->max_height = S5K5BAF_CIS_HEIGHT; 12387d459937SAndrzej Hajda return 0; 12397d459937SAndrzej Hajda } 12407d459937SAndrzej Hajda 12417d459937SAndrzej Hajda i = ARRAY_SIZE(s5k5baf_formats); 12427d459937SAndrzej Hajda while (--i) 12437d459937SAndrzej Hajda if (fse->code == s5k5baf_formats[i].code) 12447d459937SAndrzej Hajda break; 12457d459937SAndrzej Hajda fse->code = s5k5baf_formats[i].code; 12467d459937SAndrzej Hajda fse->min_width = S5K5BAF_WIN_WIDTH_MIN; 12477d459937SAndrzej Hajda fse->max_width = S5K5BAF_CIS_WIDTH; 12487d459937SAndrzej Hajda fse->max_height = S5K5BAF_WIN_HEIGHT_MIN; 12497d459937SAndrzej Hajda fse->min_height = S5K5BAF_CIS_HEIGHT; 12507d459937SAndrzej Hajda 12517d459937SAndrzej Hajda return 0; 12527d459937SAndrzej Hajda } 12537d459937SAndrzej Hajda 12547d459937SAndrzej Hajda static void s5k5baf_try_cis_format(struct v4l2_mbus_framefmt *mf) 12557d459937SAndrzej Hajda { 12567d459937SAndrzej Hajda mf->width = S5K5BAF_CIS_WIDTH; 12577d459937SAndrzej Hajda mf->height = S5K5BAF_CIS_HEIGHT; 1258f5fe58fdSBoris BREZILLON mf->code = MEDIA_BUS_FMT_FIXED; 12597d459937SAndrzej Hajda mf->colorspace = V4L2_COLORSPACE_JPEG; 12607d459937SAndrzej Hajda mf->field = V4L2_FIELD_NONE; 12617d459937SAndrzej Hajda } 12627d459937SAndrzej Hajda 12637d459937SAndrzej Hajda static int s5k5baf_try_isp_format(struct v4l2_mbus_framefmt *mf) 12647d459937SAndrzej Hajda { 12657d459937SAndrzej Hajda int pixfmt; 12667d459937SAndrzej Hajda 12677d459937SAndrzej Hajda v4l_bound_align_image(&mf->width, S5K5BAF_WIN_WIDTH_MIN, 12687d459937SAndrzej Hajda S5K5BAF_CIS_WIDTH, 1, 12697d459937SAndrzej Hajda &mf->height, S5K5BAF_WIN_HEIGHT_MIN, 12707d459937SAndrzej Hajda S5K5BAF_CIS_HEIGHT, 1, 0); 12717d459937SAndrzej Hajda 12727d459937SAndrzej Hajda pixfmt = s5k5baf_find_pixfmt(mf); 12737d459937SAndrzej Hajda 12747d459937SAndrzej Hajda mf->colorspace = s5k5baf_formats[pixfmt].colorspace; 12757d459937SAndrzej Hajda mf->code = s5k5baf_formats[pixfmt].code; 12767d459937SAndrzej Hajda mf->field = V4L2_FIELD_NONE; 12777d459937SAndrzej Hajda 12787d459937SAndrzej Hajda return pixfmt; 12797d459937SAndrzej Hajda } 12807d459937SAndrzej Hajda 1281f7234138SHans Verkuil static int s5k5baf_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, 12827d459937SAndrzej Hajda struct v4l2_subdev_format *fmt) 12837d459937SAndrzej Hajda { 12847d459937SAndrzej Hajda struct s5k5baf *state = to_s5k5baf(sd); 12857d459937SAndrzej Hajda const struct s5k5baf_pixfmt *pixfmt; 12867d459937SAndrzej Hajda struct v4l2_mbus_framefmt *mf; 12877d459937SAndrzej Hajda 12887d459937SAndrzej Hajda if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { 1289f7234138SHans Verkuil mf = v4l2_subdev_get_try_format(sd, cfg, fmt->pad); 12907d459937SAndrzej Hajda fmt->format = *mf; 12917d459937SAndrzej Hajda return 0; 12927d459937SAndrzej Hajda } 12937d459937SAndrzej Hajda 12947d459937SAndrzej Hajda mf = &fmt->format; 12957d459937SAndrzej Hajda if (fmt->pad == PAD_CIS) { 12967d459937SAndrzej Hajda s5k5baf_try_cis_format(mf); 12977d459937SAndrzej Hajda return 0; 12987d459937SAndrzej Hajda } 12997d459937SAndrzej Hajda mf->field = V4L2_FIELD_NONE; 13007d459937SAndrzej Hajda mutex_lock(&state->lock); 13017d459937SAndrzej Hajda pixfmt = &s5k5baf_formats[state->pixfmt]; 13027d459937SAndrzej Hajda mf->width = state->crop_source.width; 13037d459937SAndrzej Hajda mf->height = state->crop_source.height; 13047d459937SAndrzej Hajda mf->code = pixfmt->code; 13057d459937SAndrzej Hajda mf->colorspace = pixfmt->colorspace; 13067d459937SAndrzej Hajda mutex_unlock(&state->lock); 13077d459937SAndrzej Hajda 13087d459937SAndrzej Hajda return 0; 13097d459937SAndrzej Hajda } 13107d459937SAndrzej Hajda 1311f7234138SHans Verkuil static int s5k5baf_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, 13127d459937SAndrzej Hajda struct v4l2_subdev_format *fmt) 13137d459937SAndrzej Hajda { 13147d459937SAndrzej Hajda struct v4l2_mbus_framefmt *mf = &fmt->format; 13157d459937SAndrzej Hajda struct s5k5baf *state = to_s5k5baf(sd); 13167d459937SAndrzej Hajda const struct s5k5baf_pixfmt *pixfmt; 13177d459937SAndrzej Hajda int ret = 0; 13187d459937SAndrzej Hajda 13192f7844ecSLaurent Pinchart mf->field = V4L2_FIELD_NONE; 13202f7844ecSLaurent Pinchart 13217d459937SAndrzej Hajda if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { 1322f7234138SHans Verkuil *v4l2_subdev_get_try_format(sd, cfg, fmt->pad) = *mf; 13237d459937SAndrzej Hajda return 0; 13247d459937SAndrzej Hajda } 13257d459937SAndrzej Hajda 13267d459937SAndrzej Hajda if (fmt->pad == PAD_CIS) { 13277d459937SAndrzej Hajda s5k5baf_try_cis_format(mf); 13287d459937SAndrzej Hajda return 0; 13297d459937SAndrzej Hajda } 13307d459937SAndrzej Hajda 13317d459937SAndrzej Hajda mutex_lock(&state->lock); 13327d459937SAndrzej Hajda 13337d459937SAndrzej Hajda if (state->streaming) { 13347d459937SAndrzej Hajda mutex_unlock(&state->lock); 13357d459937SAndrzej Hajda return -EBUSY; 13367d459937SAndrzej Hajda } 13377d459937SAndrzej Hajda 13387d459937SAndrzej Hajda state->pixfmt = s5k5baf_try_isp_format(mf); 13397d459937SAndrzej Hajda pixfmt = &s5k5baf_formats[state->pixfmt]; 13407d459937SAndrzej Hajda mf->code = pixfmt->code; 13417d459937SAndrzej Hajda mf->colorspace = pixfmt->colorspace; 13427d459937SAndrzej Hajda mf->width = state->crop_source.width; 13437d459937SAndrzej Hajda mf->height = state->crop_source.height; 13447d459937SAndrzej Hajda 13457d459937SAndrzej Hajda mutex_unlock(&state->lock); 13467d459937SAndrzej Hajda return ret; 13477d459937SAndrzej Hajda } 13487d459937SAndrzej Hajda 13497d459937SAndrzej Hajda enum selection_rect { R_CIS, R_CROP_SINK, R_COMPOSE, R_CROP_SOURCE, R_INVALID }; 13507d459937SAndrzej Hajda 13517d459937SAndrzej Hajda static enum selection_rect s5k5baf_get_sel_rect(u32 pad, u32 target) 13527d459937SAndrzej Hajda { 13537d459937SAndrzej Hajda switch (target) { 13547d459937SAndrzej Hajda case V4L2_SEL_TGT_CROP_BOUNDS: 13557d459937SAndrzej Hajda return pad ? R_COMPOSE : R_CIS; 13567d459937SAndrzej Hajda case V4L2_SEL_TGT_CROP: 13577d459937SAndrzej Hajda return pad ? R_CROP_SOURCE : R_CROP_SINK; 13587d459937SAndrzej Hajda case V4L2_SEL_TGT_COMPOSE_BOUNDS: 13597d459937SAndrzej Hajda return pad ? R_INVALID : R_CROP_SINK; 13607d459937SAndrzej Hajda case V4L2_SEL_TGT_COMPOSE: 13617d459937SAndrzej Hajda return pad ? R_INVALID : R_COMPOSE; 13627d459937SAndrzej Hajda default: 13637d459937SAndrzej Hajda return R_INVALID; 13647d459937SAndrzej Hajda } 13657d459937SAndrzej Hajda } 13667d459937SAndrzej Hajda 13677d459937SAndrzej Hajda static int s5k5baf_is_bound_target(u32 target) 13687d459937SAndrzej Hajda { 13697296e158SSachin Kamat return target == V4L2_SEL_TGT_CROP_BOUNDS || 13707296e158SSachin Kamat target == V4L2_SEL_TGT_COMPOSE_BOUNDS; 13717d459937SAndrzej Hajda } 13727d459937SAndrzej Hajda 13737d459937SAndrzej Hajda static int s5k5baf_get_selection(struct v4l2_subdev *sd, 1374f7234138SHans Verkuil struct v4l2_subdev_pad_config *cfg, 13757d459937SAndrzej Hajda struct v4l2_subdev_selection *sel) 13767d459937SAndrzej Hajda { 1377281643a2SGustavo A. R. Silva enum selection_rect rtype; 13787d459937SAndrzej Hajda struct s5k5baf *state = to_s5k5baf(sd); 13797d459937SAndrzej Hajda 13807d459937SAndrzej Hajda rtype = s5k5baf_get_sel_rect(sel->pad, sel->target); 13817d459937SAndrzej Hajda 13827d459937SAndrzej Hajda switch (rtype) { 13837d459937SAndrzej Hajda case R_INVALID: 13847d459937SAndrzej Hajda return -EINVAL; 13857d459937SAndrzej Hajda case R_CIS: 13867d459937SAndrzej Hajda sel->r = s5k5baf_cis_rect; 13877d459937SAndrzej Hajda return 0; 13887d459937SAndrzej Hajda default: 13897d459937SAndrzej Hajda break; 13907d459937SAndrzej Hajda } 13917d459937SAndrzej Hajda 13927d459937SAndrzej Hajda if (sel->which == V4L2_SUBDEV_FORMAT_TRY) { 13937d459937SAndrzej Hajda if (rtype == R_COMPOSE) 1394f7234138SHans Verkuil sel->r = *v4l2_subdev_get_try_compose(sd, cfg, sel->pad); 13957d459937SAndrzej Hajda else 1396f7234138SHans Verkuil sel->r = *v4l2_subdev_get_try_crop(sd, cfg, sel->pad); 13977d459937SAndrzej Hajda return 0; 13987d459937SAndrzej Hajda } 13997d459937SAndrzej Hajda 14007d459937SAndrzej Hajda mutex_lock(&state->lock); 14017d459937SAndrzej Hajda switch (rtype) { 14027d459937SAndrzej Hajda case R_CROP_SINK: 14037d459937SAndrzej Hajda sel->r = state->crop_sink; 14047d459937SAndrzej Hajda break; 14057d459937SAndrzej Hajda case R_COMPOSE: 14067d459937SAndrzej Hajda sel->r = state->compose; 14077d459937SAndrzej Hajda break; 14087d459937SAndrzej Hajda case R_CROP_SOURCE: 14097d459937SAndrzej Hajda sel->r = state->crop_source; 14107d459937SAndrzej Hajda break; 14117d459937SAndrzej Hajda default: 14127d459937SAndrzej Hajda break; 14137d459937SAndrzej Hajda } 14147d459937SAndrzej Hajda if (s5k5baf_is_bound_target(sel->target)) { 14157d459937SAndrzej Hajda sel->r.left = 0; 14167d459937SAndrzej Hajda sel->r.top = 0; 14177d459937SAndrzej Hajda } 14187d459937SAndrzej Hajda mutex_unlock(&state->lock); 14197d459937SAndrzej Hajda 14207d459937SAndrzej Hajda return 0; 14217d459937SAndrzej Hajda } 14227d459937SAndrzej Hajda 14237d459937SAndrzej Hajda /* bounds range [start, start+len) to [0, max) and aligns to 2 */ 14247d459937SAndrzej Hajda static void s5k5baf_bound_range(u32 *start, u32 *len, u32 max) 14257d459937SAndrzej Hajda { 14267d459937SAndrzej Hajda if (*len > max) 14277d459937SAndrzej Hajda *len = max; 14287d459937SAndrzej Hajda if (*start + *len > max) 14297d459937SAndrzej Hajda *start = max - *len; 14307d459937SAndrzej Hajda *start &= ~1; 14317d459937SAndrzej Hajda *len &= ~1; 14327d459937SAndrzej Hajda if (*len < S5K5BAF_WIN_WIDTH_MIN) 14337d459937SAndrzej Hajda *len = S5K5BAF_WIN_WIDTH_MIN; 14347d459937SAndrzej Hajda } 14357d459937SAndrzej Hajda 14367d459937SAndrzej Hajda static void s5k5baf_bound_rect(struct v4l2_rect *r, u32 width, u32 height) 14377d459937SAndrzej Hajda { 14387d459937SAndrzej Hajda s5k5baf_bound_range(&r->left, &r->width, width); 14397d459937SAndrzej Hajda s5k5baf_bound_range(&r->top, &r->height, height); 14407d459937SAndrzej Hajda } 14417d459937SAndrzej Hajda 14427d459937SAndrzej Hajda static void s5k5baf_set_rect_and_adjust(struct v4l2_rect **rects, 14437d459937SAndrzej Hajda enum selection_rect first, 14447d459937SAndrzej Hajda struct v4l2_rect *v) 14457d459937SAndrzej Hajda { 14467d459937SAndrzej Hajda struct v4l2_rect *r, *br; 14477d459937SAndrzej Hajda enum selection_rect i = first; 14487d459937SAndrzej Hajda 14497d459937SAndrzej Hajda *rects[first] = *v; 14507d459937SAndrzej Hajda do { 14517d459937SAndrzej Hajda r = rects[i]; 14527d459937SAndrzej Hajda br = rects[i - 1]; 14537d459937SAndrzej Hajda s5k5baf_bound_rect(r, br->width, br->height); 14547d459937SAndrzej Hajda } while (++i != R_INVALID); 14557d459937SAndrzej Hajda *v = *rects[first]; 14567d459937SAndrzej Hajda } 14577d459937SAndrzej Hajda 14587d459937SAndrzej Hajda static bool s5k5baf_cmp_rect(const struct v4l2_rect *r1, 14597d459937SAndrzej Hajda const struct v4l2_rect *r2) 14607d459937SAndrzej Hajda { 14617d459937SAndrzej Hajda return !memcmp(r1, r2, sizeof(*r1)); 14627d459937SAndrzej Hajda } 14637d459937SAndrzej Hajda 14647d459937SAndrzej Hajda static int s5k5baf_set_selection(struct v4l2_subdev *sd, 1465f7234138SHans Verkuil struct v4l2_subdev_pad_config *cfg, 14667d459937SAndrzej Hajda struct v4l2_subdev_selection *sel) 14677d459937SAndrzej Hajda { 14687d459937SAndrzej Hajda static enum selection_rect rtype; 14697d459937SAndrzej Hajda struct s5k5baf *state = to_s5k5baf(sd); 14707d459937SAndrzej Hajda struct v4l2_rect **rects; 14717d459937SAndrzej Hajda int ret = 0; 14727d459937SAndrzej Hajda 14737d459937SAndrzej Hajda rtype = s5k5baf_get_sel_rect(sel->pad, sel->target); 14747d459937SAndrzej Hajda if (rtype == R_INVALID || s5k5baf_is_bound_target(sel->target)) 14757d459937SAndrzej Hajda return -EINVAL; 14767d459937SAndrzej Hajda 14777d459937SAndrzej Hajda /* allow only scaling on compose */ 14787d459937SAndrzej Hajda if (rtype == R_COMPOSE) { 14797d459937SAndrzej Hajda sel->r.left = 0; 14807d459937SAndrzej Hajda sel->r.top = 0; 14817d459937SAndrzej Hajda } 14827d459937SAndrzej Hajda 14837d459937SAndrzej Hajda if (sel->which == V4L2_SUBDEV_FORMAT_TRY) { 14847d459937SAndrzej Hajda rects = (struct v4l2_rect * []) { 14857d459937SAndrzej Hajda &s5k5baf_cis_rect, 1486f7234138SHans Verkuil v4l2_subdev_get_try_crop(sd, cfg, PAD_CIS), 1487f7234138SHans Verkuil v4l2_subdev_get_try_compose(sd, cfg, PAD_CIS), 1488f7234138SHans Verkuil v4l2_subdev_get_try_crop(sd, cfg, PAD_OUT) 14897d459937SAndrzej Hajda }; 14907d459937SAndrzej Hajda s5k5baf_set_rect_and_adjust(rects, rtype, &sel->r); 14917d459937SAndrzej Hajda return 0; 14927d459937SAndrzej Hajda } 14937d459937SAndrzej Hajda 14947d459937SAndrzej Hajda rects = (struct v4l2_rect * []) { 14957d459937SAndrzej Hajda &s5k5baf_cis_rect, 14967d459937SAndrzej Hajda &state->crop_sink, 14977d459937SAndrzej Hajda &state->compose, 14987d459937SAndrzej Hajda &state->crop_source 14997d459937SAndrzej Hajda }; 15007d459937SAndrzej Hajda mutex_lock(&state->lock); 15017d459937SAndrzej Hajda if (state->streaming) { 15027d459937SAndrzej Hajda /* adjust sel->r to avoid output resolution change */ 15037d459937SAndrzej Hajda if (rtype < R_CROP_SOURCE) { 15047d459937SAndrzej Hajda if (sel->r.width < state->crop_source.width) 15057d459937SAndrzej Hajda sel->r.width = state->crop_source.width; 15067d459937SAndrzej Hajda if (sel->r.height < state->crop_source.height) 15077d459937SAndrzej Hajda sel->r.height = state->crop_source.height; 15087d459937SAndrzej Hajda } else { 15097d459937SAndrzej Hajda sel->r.width = state->crop_source.width; 15107d459937SAndrzej Hajda sel->r.height = state->crop_source.height; 15117d459937SAndrzej Hajda } 15127d459937SAndrzej Hajda } 15137d459937SAndrzej Hajda s5k5baf_set_rect_and_adjust(rects, rtype, &sel->r); 15147d459937SAndrzej Hajda if (!s5k5baf_cmp_rect(&state->crop_sink, &s5k5baf_cis_rect) || 15157d459937SAndrzej Hajda !s5k5baf_cmp_rect(&state->compose, &s5k5baf_cis_rect)) 15167d459937SAndrzej Hajda state->apply_crop = 1; 15177d459937SAndrzej Hajda if (state->streaming) 15187d459937SAndrzej Hajda ret = s5k5baf_hw_set_crop_rects(state); 15197d459937SAndrzej Hajda mutex_unlock(&state->lock); 15207d459937SAndrzej Hajda 15217d459937SAndrzej Hajda return ret; 15227d459937SAndrzej Hajda } 15237d459937SAndrzej Hajda 15247d459937SAndrzej Hajda static const struct v4l2_subdev_pad_ops s5k5baf_cis_pad_ops = { 15257d459937SAndrzej Hajda .enum_mbus_code = s5k5baf_enum_mbus_code, 15267d459937SAndrzej Hajda .enum_frame_size = s5k5baf_enum_frame_size, 15277d459937SAndrzej Hajda .get_fmt = s5k5baf_get_fmt, 15287d459937SAndrzej Hajda .set_fmt = s5k5baf_set_fmt, 15297d459937SAndrzej Hajda }; 15307d459937SAndrzej Hajda 15317d459937SAndrzej Hajda static const struct v4l2_subdev_pad_ops s5k5baf_pad_ops = { 15327d459937SAndrzej Hajda .enum_mbus_code = s5k5baf_enum_mbus_code, 15337d459937SAndrzej Hajda .enum_frame_size = s5k5baf_enum_frame_size, 15347d459937SAndrzej Hajda .enum_frame_interval = s5k5baf_enum_frame_interval, 15357d459937SAndrzej Hajda .get_fmt = s5k5baf_get_fmt, 15367d459937SAndrzej Hajda .set_fmt = s5k5baf_set_fmt, 15377d459937SAndrzej Hajda .get_selection = s5k5baf_get_selection, 15387d459937SAndrzej Hajda .set_selection = s5k5baf_set_selection, 15397d459937SAndrzej Hajda }; 15407d459937SAndrzej Hajda 15417d459937SAndrzej Hajda static const struct v4l2_subdev_video_ops s5k5baf_video_ops = { 15427d459937SAndrzej Hajda .g_frame_interval = s5k5baf_g_frame_interval, 15437d459937SAndrzej Hajda .s_frame_interval = s5k5baf_s_frame_interval, 15447d459937SAndrzej Hajda .s_stream = s5k5baf_s_stream, 15457d459937SAndrzej Hajda }; 15467d459937SAndrzej Hajda 15477d459937SAndrzej Hajda /* 15487d459937SAndrzej Hajda * V4L2 subdev controls 15497d459937SAndrzej Hajda */ 15507d459937SAndrzej Hajda 15517d459937SAndrzej Hajda static int s5k5baf_s_ctrl(struct v4l2_ctrl *ctrl) 15527d459937SAndrzej Hajda { 15537d459937SAndrzej Hajda struct v4l2_subdev *sd = ctrl_to_sd(ctrl); 15547d459937SAndrzej Hajda struct s5k5baf *state = to_s5k5baf(sd); 15557d459937SAndrzej Hajda int ret; 15567d459937SAndrzej Hajda 15577d459937SAndrzej Hajda v4l2_dbg(1, debug, sd, "ctrl: %s, value: %d\n", ctrl->name, ctrl->val); 15587d459937SAndrzej Hajda 15597d459937SAndrzej Hajda mutex_lock(&state->lock); 15607d459937SAndrzej Hajda 15617d459937SAndrzej Hajda if (state->power == 0) 15627d459937SAndrzej Hajda goto unlock; 15637d459937SAndrzej Hajda 15647d459937SAndrzej Hajda switch (ctrl->id) { 15657d459937SAndrzej Hajda case V4L2_CID_AUTO_WHITE_BALANCE: 15667d459937SAndrzej Hajda s5k5baf_hw_set_awb(state, ctrl->val); 15677d459937SAndrzej Hajda break; 15687d459937SAndrzej Hajda 15697d459937SAndrzej Hajda case V4L2_CID_BRIGHTNESS: 15707d459937SAndrzej Hajda s5k5baf_write(state, REG_USER_BRIGHTNESS, ctrl->val); 15717d459937SAndrzej Hajda break; 15727d459937SAndrzej Hajda 15737d459937SAndrzej Hajda case V4L2_CID_COLORFX: 15747d459937SAndrzej Hajda s5k5baf_hw_set_colorfx(state, ctrl->val); 15757d459937SAndrzej Hajda break; 15767d459937SAndrzej Hajda 15777d459937SAndrzej Hajda case V4L2_CID_CONTRAST: 15787d459937SAndrzej Hajda s5k5baf_write(state, REG_USER_CONTRAST, ctrl->val); 15797d459937SAndrzej Hajda break; 15807d459937SAndrzej Hajda 15817d459937SAndrzej Hajda case V4L2_CID_EXPOSURE_AUTO: 15827d459937SAndrzej Hajda s5k5baf_hw_set_auto_exposure(state, ctrl->val); 15837d459937SAndrzej Hajda break; 15847d459937SAndrzej Hajda 15857d459937SAndrzej Hajda case V4L2_CID_HFLIP: 15867d459937SAndrzej Hajda s5k5baf_hw_set_mirror(state); 15877d459937SAndrzej Hajda break; 15887d459937SAndrzej Hajda 15897d459937SAndrzej Hajda case V4L2_CID_POWER_LINE_FREQUENCY: 15907d459937SAndrzej Hajda s5k5baf_hw_set_anti_flicker(state, ctrl->val); 15917d459937SAndrzej Hajda break; 15927d459937SAndrzej Hajda 15937d459937SAndrzej Hajda case V4L2_CID_SATURATION: 15947d459937SAndrzej Hajda s5k5baf_write(state, REG_USER_SATURATION, ctrl->val); 15957d459937SAndrzej Hajda break; 15967d459937SAndrzej Hajda 15977d459937SAndrzej Hajda case V4L2_CID_SHARPNESS: 15987d459937SAndrzej Hajda s5k5baf_write(state, REG_USER_SHARPBLUR, ctrl->val); 15997d459937SAndrzej Hajda break; 16007d459937SAndrzej Hajda 16017d459937SAndrzej Hajda case V4L2_CID_WHITE_BALANCE_TEMPERATURE: 16027d459937SAndrzej Hajda s5k5baf_write(state, REG_P_COLORTEMP(0), ctrl->val); 16037d459937SAndrzej Hajda if (state->apply_cfg) 16047d459937SAndrzej Hajda s5k5baf_hw_sync_cfg(state); 16057d459937SAndrzej Hajda break; 16067d459937SAndrzej Hajda 16077d459937SAndrzej Hajda case V4L2_CID_TEST_PATTERN: 16087d459937SAndrzej Hajda s5k5baf_hw_set_test_pattern(state, ctrl->val); 16097d459937SAndrzej Hajda break; 16107d459937SAndrzej Hajda } 16117d459937SAndrzej Hajda unlock: 16127d459937SAndrzej Hajda ret = s5k5baf_clear_error(state); 16137d459937SAndrzej Hajda mutex_unlock(&state->lock); 16147d459937SAndrzej Hajda return ret; 16157d459937SAndrzej Hajda } 16167d459937SAndrzej Hajda 16177d459937SAndrzej Hajda static const struct v4l2_ctrl_ops s5k5baf_ctrl_ops = { 16187d459937SAndrzej Hajda .s_ctrl = s5k5baf_s_ctrl, 16197d459937SAndrzej Hajda }; 16207d459937SAndrzej Hajda 16217d459937SAndrzej Hajda static const char * const s5k5baf_test_pattern_menu[] = { 16227d459937SAndrzej Hajda "Disabled", 16237d459937SAndrzej Hajda "Blank", 16247d459937SAndrzej Hajda "Bars", 16257d459937SAndrzej Hajda "Gradients", 16267d459937SAndrzej Hajda "Textile", 16277d459937SAndrzej Hajda "Textile2", 16287d459937SAndrzej Hajda "Squares" 16297d459937SAndrzej Hajda }; 16307d459937SAndrzej Hajda 16317d459937SAndrzej Hajda static int s5k5baf_initialize_ctrls(struct s5k5baf *state) 16327d459937SAndrzej Hajda { 16337d459937SAndrzej Hajda const struct v4l2_ctrl_ops *ops = &s5k5baf_ctrl_ops; 16347d459937SAndrzej Hajda struct s5k5baf_ctrls *ctrls = &state->ctrls; 16357d459937SAndrzej Hajda struct v4l2_ctrl_handler *hdl = &ctrls->handler; 16367d459937SAndrzej Hajda int ret; 16377d459937SAndrzej Hajda 16387d459937SAndrzej Hajda ret = v4l2_ctrl_handler_init(hdl, 16); 16397d459937SAndrzej Hajda if (ret < 0) { 16407d459937SAndrzej Hajda v4l2_err(&state->sd, "cannot init ctrl handler (%d)\n", ret); 16417d459937SAndrzej Hajda return ret; 16427d459937SAndrzej Hajda } 16437d459937SAndrzej Hajda 16447d459937SAndrzej Hajda /* Auto white balance cluster */ 16457d459937SAndrzej Hajda ctrls->awb = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_AUTO_WHITE_BALANCE, 16467d459937SAndrzej Hajda 0, 1, 1, 1); 16477d459937SAndrzej Hajda ctrls->gain_red = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_RED_BALANCE, 16487d459937SAndrzej Hajda 0, 255, 1, S5K5BAF_GAIN_RED_DEF); 16497d459937SAndrzej Hajda ctrls->gain_blue = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_BLUE_BALANCE, 16507d459937SAndrzej Hajda 0, 255, 1, S5K5BAF_GAIN_BLUE_DEF); 16517d459937SAndrzej Hajda v4l2_ctrl_auto_cluster(3, &ctrls->awb, 0, false); 16527d459937SAndrzej Hajda 16537d459937SAndrzej Hajda ctrls->hflip = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_HFLIP, 0, 1, 1, 0); 16547d459937SAndrzej Hajda ctrls->vflip = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_VFLIP, 0, 1, 1, 0); 16557d459937SAndrzej Hajda v4l2_ctrl_cluster(2, &ctrls->hflip); 16567d459937SAndrzej Hajda 16577d459937SAndrzej Hajda ctrls->auto_exp = v4l2_ctrl_new_std_menu(hdl, ops, 16587d459937SAndrzej Hajda V4L2_CID_EXPOSURE_AUTO, 16597d459937SAndrzej Hajda V4L2_EXPOSURE_MANUAL, 0, V4L2_EXPOSURE_AUTO); 16607d459937SAndrzej Hajda /* Exposure time: x 1 us */ 16617d459937SAndrzej Hajda ctrls->exposure = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_EXPOSURE, 16627d459937SAndrzej Hajda 0, 6000000U, 1, 100000U); 16637d459937SAndrzej Hajda /* Total gain: 256 <=> 1x */ 16647d459937SAndrzej Hajda ctrls->gain = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_GAIN, 16657d459937SAndrzej Hajda 0, 256, 1, 256); 16667d459937SAndrzej Hajda v4l2_ctrl_auto_cluster(3, &ctrls->auto_exp, 0, false); 16677d459937SAndrzej Hajda 16687d459937SAndrzej Hajda v4l2_ctrl_new_std_menu(hdl, ops, V4L2_CID_POWER_LINE_FREQUENCY, 16697d459937SAndrzej Hajda V4L2_CID_POWER_LINE_FREQUENCY_AUTO, 0, 16707d459937SAndrzej Hajda V4L2_CID_POWER_LINE_FREQUENCY_AUTO); 16717d459937SAndrzej Hajda 16727d459937SAndrzej Hajda v4l2_ctrl_new_std_menu(hdl, ops, V4L2_CID_COLORFX, 16737d459937SAndrzej Hajda V4L2_COLORFX_SKY_BLUE, ~0x6f, V4L2_COLORFX_NONE); 16747d459937SAndrzej Hajda 16757d459937SAndrzej Hajda v4l2_ctrl_new_std(hdl, ops, V4L2_CID_WHITE_BALANCE_TEMPERATURE, 16767d459937SAndrzej Hajda 0, 256, 1, 0); 16777d459937SAndrzej Hajda 16787d459937SAndrzej Hajda v4l2_ctrl_new_std(hdl, ops, V4L2_CID_SATURATION, -127, 127, 1, 0); 16797d459937SAndrzej Hajda v4l2_ctrl_new_std(hdl, ops, V4L2_CID_BRIGHTNESS, -127, 127, 1, 0); 16807d459937SAndrzej Hajda v4l2_ctrl_new_std(hdl, ops, V4L2_CID_CONTRAST, -127, 127, 1, 0); 16817d459937SAndrzej Hajda v4l2_ctrl_new_std(hdl, ops, V4L2_CID_SHARPNESS, -127, 127, 1, 0); 16827d459937SAndrzej Hajda 16837d459937SAndrzej Hajda v4l2_ctrl_new_std_menu_items(hdl, ops, V4L2_CID_TEST_PATTERN, 16847d459937SAndrzej Hajda ARRAY_SIZE(s5k5baf_test_pattern_menu) - 1, 16857d459937SAndrzej Hajda 0, 0, s5k5baf_test_pattern_menu); 16867d459937SAndrzej Hajda 16877d459937SAndrzej Hajda if (hdl->error) { 16887d459937SAndrzej Hajda v4l2_err(&state->sd, "error creating controls (%d)\n", 16897d459937SAndrzej Hajda hdl->error); 16907d459937SAndrzej Hajda ret = hdl->error; 16917d459937SAndrzej Hajda v4l2_ctrl_handler_free(hdl); 16927d459937SAndrzej Hajda return ret; 16937d459937SAndrzej Hajda } 16947d459937SAndrzej Hajda 16957d459937SAndrzej Hajda state->sd.ctrl_handler = hdl; 16967d459937SAndrzej Hajda return 0; 16977d459937SAndrzej Hajda } 16987d459937SAndrzej Hajda 16997d459937SAndrzej Hajda /* 17007d459937SAndrzej Hajda * V4L2 subdev internal operations 17017d459937SAndrzej Hajda */ 17027d459937SAndrzej Hajda static int s5k5baf_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) 17037d459937SAndrzej Hajda { 17047d459937SAndrzej Hajda struct v4l2_mbus_framefmt *mf; 17057d459937SAndrzej Hajda 1706f7234138SHans Verkuil mf = v4l2_subdev_get_try_format(sd, fh->pad, PAD_CIS); 17077d459937SAndrzej Hajda s5k5baf_try_cis_format(mf); 17087d459937SAndrzej Hajda 17097d459937SAndrzej Hajda if (s5k5baf_is_cis_subdev(sd)) 17107d459937SAndrzej Hajda return 0; 17117d459937SAndrzej Hajda 1712f7234138SHans Verkuil mf = v4l2_subdev_get_try_format(sd, fh->pad, PAD_OUT); 17137d459937SAndrzej Hajda mf->colorspace = s5k5baf_formats[0].colorspace; 17147d459937SAndrzej Hajda mf->code = s5k5baf_formats[0].code; 17157d459937SAndrzej Hajda mf->width = s5k5baf_cis_rect.width; 17167d459937SAndrzej Hajda mf->height = s5k5baf_cis_rect.height; 17177d459937SAndrzej Hajda mf->field = V4L2_FIELD_NONE; 17187d459937SAndrzej Hajda 1719f7234138SHans Verkuil *v4l2_subdev_get_try_crop(sd, fh->pad, PAD_CIS) = s5k5baf_cis_rect; 1720f7234138SHans Verkuil *v4l2_subdev_get_try_compose(sd, fh->pad, PAD_CIS) = s5k5baf_cis_rect; 1721f7234138SHans Verkuil *v4l2_subdev_get_try_crop(sd, fh->pad, PAD_OUT) = s5k5baf_cis_rect; 17227d459937SAndrzej Hajda 17237d459937SAndrzej Hajda return 0; 17247d459937SAndrzej Hajda } 17257d459937SAndrzej Hajda 17267d459937SAndrzej Hajda static int s5k5baf_check_fw_revision(struct s5k5baf *state) 17277d459937SAndrzej Hajda { 17287d459937SAndrzej Hajda u16 api_ver = 0, fw_rev = 0, s_id = 0; 17297d459937SAndrzej Hajda int ret; 17307d459937SAndrzej Hajda 17317d459937SAndrzej Hajda api_ver = s5k5baf_read(state, REG_FW_APIVER); 17327d459937SAndrzej Hajda fw_rev = s5k5baf_read(state, REG_FW_REVISION) & 0xff; 17337d459937SAndrzej Hajda s_id = s5k5baf_read(state, REG_FW_SENSOR_ID); 17347d459937SAndrzej Hajda ret = s5k5baf_clear_error(state); 17357d459937SAndrzej Hajda if (ret < 0) 17367d459937SAndrzej Hajda return ret; 17377d459937SAndrzej Hajda 17387d459937SAndrzej Hajda v4l2_info(&state->sd, "FW API=%#x, revision=%#x sensor_id=%#x\n", 17397d459937SAndrzej Hajda api_ver, fw_rev, s_id); 17407d459937SAndrzej Hajda 17417d459937SAndrzej Hajda if (api_ver != S5K5BAF_FW_APIVER) { 17427d459937SAndrzej Hajda v4l2_err(&state->sd, "FW API version not supported\n"); 17437d459937SAndrzej Hajda return -ENODEV; 17447d459937SAndrzej Hajda } 17457d459937SAndrzej Hajda 17467d459937SAndrzej Hajda return 0; 17477d459937SAndrzej Hajda } 17487d459937SAndrzej Hajda 17497d459937SAndrzej Hajda static int s5k5baf_registered(struct v4l2_subdev *sd) 17507d459937SAndrzej Hajda { 17517d459937SAndrzej Hajda struct s5k5baf *state = to_s5k5baf(sd); 17527d459937SAndrzej Hajda int ret; 17537d459937SAndrzej Hajda 17547d459937SAndrzej Hajda ret = v4l2_device_register_subdev(sd->v4l2_dev, &state->cis_sd); 17557d459937SAndrzej Hajda if (ret < 0) 17567d459937SAndrzej Hajda v4l2_err(sd, "failed to register subdev %s\n", 17577d459937SAndrzej Hajda state->cis_sd.name); 17587d459937SAndrzej Hajda else 17598df00a15SMauro Carvalho Chehab ret = media_create_pad_link(&state->cis_sd.entity, PAD_CIS, 17607d459937SAndrzej Hajda &state->sd.entity, PAD_CIS, 17617d459937SAndrzej Hajda MEDIA_LNK_FL_IMMUTABLE | 17627d459937SAndrzej Hajda MEDIA_LNK_FL_ENABLED); 17637d459937SAndrzej Hajda return ret; 17647d459937SAndrzej Hajda } 17657d459937SAndrzej Hajda 17667d459937SAndrzej Hajda static void s5k5baf_unregistered(struct v4l2_subdev *sd) 17677d459937SAndrzej Hajda { 17687d459937SAndrzej Hajda struct s5k5baf *state = to_s5k5baf(sd); 17697d459937SAndrzej Hajda v4l2_device_unregister_subdev(&state->cis_sd); 17707d459937SAndrzej Hajda } 17717d459937SAndrzej Hajda 17727d459937SAndrzej Hajda static const struct v4l2_subdev_ops s5k5baf_cis_subdev_ops = { 17737d459937SAndrzej Hajda .pad = &s5k5baf_cis_pad_ops, 17747d459937SAndrzej Hajda }; 17757d459937SAndrzej Hajda 17767d459937SAndrzej Hajda static const struct v4l2_subdev_internal_ops s5k5baf_cis_subdev_internal_ops = { 17777d459937SAndrzej Hajda .open = s5k5baf_open, 17787d459937SAndrzej Hajda }; 17797d459937SAndrzej Hajda 17807d459937SAndrzej Hajda static const struct v4l2_subdev_internal_ops s5k5baf_subdev_internal_ops = { 17817d459937SAndrzej Hajda .registered = s5k5baf_registered, 17827d459937SAndrzej Hajda .unregistered = s5k5baf_unregistered, 17837d459937SAndrzej Hajda .open = s5k5baf_open, 17847d459937SAndrzej Hajda }; 17857d459937SAndrzej Hajda 17867d459937SAndrzej Hajda static const struct v4l2_subdev_core_ops s5k5baf_core_ops = { 17877d459937SAndrzej Hajda .s_power = s5k5baf_set_power, 17887d459937SAndrzej Hajda .log_status = v4l2_ctrl_subdev_log_status, 17897d459937SAndrzej Hajda }; 17907d459937SAndrzej Hajda 17917d459937SAndrzej Hajda static const struct v4l2_subdev_ops s5k5baf_subdev_ops = { 17927d459937SAndrzej Hajda .core = &s5k5baf_core_ops, 17937d459937SAndrzej Hajda .pad = &s5k5baf_pad_ops, 17947d459937SAndrzej Hajda .video = &s5k5baf_video_ops, 17957d459937SAndrzej Hajda }; 17967d459937SAndrzej Hajda 17977d459937SAndrzej Hajda static int s5k5baf_configure_gpios(struct s5k5baf *state) 17987d459937SAndrzej Hajda { 17990348bb1aSHans Verkuil static const char * const name[] = { "S5K5BAF_STBY", "S5K5BAF_RST" }; 18007d459937SAndrzej Hajda struct i2c_client *c = v4l2_get_subdevdata(&state->sd); 18017d459937SAndrzej Hajda struct s5k5baf_gpio *g = state->gpios; 18027d459937SAndrzej Hajda int ret, i; 18037d459937SAndrzej Hajda 18047d459937SAndrzej Hajda for (i = 0; i < NUM_GPIOS; ++i) { 18057d459937SAndrzej Hajda int flags = GPIOF_DIR_OUT; 18067d459937SAndrzej Hajda if (g[i].level) 18077d459937SAndrzej Hajda flags |= GPIOF_INIT_HIGH; 18087d459937SAndrzej Hajda ret = devm_gpio_request_one(&c->dev, g[i].gpio, flags, name[i]); 18097d459937SAndrzej Hajda if (ret < 0) { 18107d459937SAndrzej Hajda v4l2_err(c, "failed to request gpio %s\n", name[i]); 18117d459937SAndrzej Hajda return ret; 18127d459937SAndrzej Hajda } 18137d459937SAndrzej Hajda } 18147d459937SAndrzej Hajda return 0; 18157d459937SAndrzej Hajda } 18167d459937SAndrzej Hajda 18177d459937SAndrzej Hajda static int s5k5baf_parse_gpios(struct s5k5baf_gpio *gpios, struct device *dev) 18187d459937SAndrzej Hajda { 18197d459937SAndrzej Hajda static const char * const names[] = { 18207d459937SAndrzej Hajda "stbyn-gpios", 18217d459937SAndrzej Hajda "rstn-gpios", 18227d459937SAndrzej Hajda }; 18237d459937SAndrzej Hajda struct device_node *node = dev->of_node; 18247d459937SAndrzej Hajda enum of_gpio_flags flags; 18257d459937SAndrzej Hajda int ret, i; 18267d459937SAndrzej Hajda 18277d459937SAndrzej Hajda for (i = 0; i < NUM_GPIOS; ++i) { 18287d459937SAndrzej Hajda ret = of_get_named_gpio_flags(node, names[i], 0, &flags); 18297d459937SAndrzej Hajda if (ret < 0) { 18307d459937SAndrzej Hajda dev_err(dev, "no %s GPIO pin provided\n", names[i]); 18317d459937SAndrzej Hajda return ret; 18327d459937SAndrzej Hajda } 18337d459937SAndrzej Hajda gpios[i].gpio = ret; 18347d459937SAndrzej Hajda gpios[i].level = !(flags & OF_GPIO_ACTIVE_LOW); 18357d459937SAndrzej Hajda } 18367d459937SAndrzej Hajda 18377d459937SAndrzej Hajda return 0; 18387d459937SAndrzej Hajda } 18397d459937SAndrzej Hajda 18407d459937SAndrzej Hajda static int s5k5baf_parse_device_node(struct s5k5baf *state, struct device *dev) 18417d459937SAndrzej Hajda { 18427d459937SAndrzej Hajda struct device_node *node = dev->of_node; 18437d459937SAndrzej Hajda struct device_node *node_ep; 1844859969b3SSakari Ailus struct v4l2_fwnode_endpoint ep; 18457d459937SAndrzej Hajda int ret; 18467d459937SAndrzej Hajda 18477d459937SAndrzej Hajda if (!node) { 18487d459937SAndrzej Hajda dev_err(dev, "no device-tree node provided\n"); 18497d459937SAndrzej Hajda return -EINVAL; 18507d459937SAndrzej Hajda } 18517d459937SAndrzej Hajda 18527d459937SAndrzej Hajda ret = of_property_read_u32(node, "clock-frequency", 18537d459937SAndrzej Hajda &state->mclk_frequency); 18547d459937SAndrzej Hajda if (ret < 0) { 18557d459937SAndrzej Hajda state->mclk_frequency = S5K5BAF_DEFAULT_MCLK_FREQ; 18567d459937SAndrzej Hajda dev_info(dev, "using default %u Hz clock frequency\n", 18577d459937SAndrzej Hajda state->mclk_frequency); 18587d459937SAndrzej Hajda } 18597d459937SAndrzej Hajda 18607d459937SAndrzej Hajda ret = s5k5baf_parse_gpios(state->gpios, dev); 18617d459937SAndrzej Hajda if (ret < 0) 18627d459937SAndrzej Hajda return ret; 18637d459937SAndrzej Hajda 1864fd9fdb78SPhilipp Zabel node_ep = of_graph_get_next_endpoint(node, NULL); 18657d459937SAndrzej Hajda if (!node_ep) { 186668d9c47bSRob Herring dev_err(dev, "no endpoint defined at node %pOF\n", node); 18677d459937SAndrzej Hajda return -EINVAL; 18687d459937SAndrzej Hajda } 18697d459937SAndrzej Hajda 1870859969b3SSakari Ailus ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(node_ep), &ep); 18717d459937SAndrzej Hajda of_node_put(node_ep); 18722388309cSJavier Martinez Canillas if (ret) 18732388309cSJavier Martinez Canillas return ret; 18742388309cSJavier Martinez Canillas 18757d459937SAndrzej Hajda state->bus_type = ep.bus_type; 18767d459937SAndrzej Hajda 18777d459937SAndrzej Hajda switch (state->bus_type) { 18787d459937SAndrzej Hajda case V4L2_MBUS_CSI2: 18797d459937SAndrzej Hajda state->nlanes = ep.bus.mipi_csi2.num_data_lanes; 18807d459937SAndrzej Hajda break; 18817d459937SAndrzej Hajda case V4L2_MBUS_PARALLEL: 18827d459937SAndrzej Hajda break; 18837d459937SAndrzej Hajda default: 188468d9c47bSRob Herring dev_err(dev, "unsupported bus in endpoint defined at node %pOF\n", 188568d9c47bSRob Herring node); 18867d459937SAndrzej Hajda return -EINVAL; 18877d459937SAndrzej Hajda } 18887d459937SAndrzej Hajda 18897d459937SAndrzej Hajda return 0; 18907d459937SAndrzej Hajda } 18917d459937SAndrzej Hajda 18927d459937SAndrzej Hajda static int s5k5baf_configure_subdevs(struct s5k5baf *state, 18937d459937SAndrzej Hajda struct i2c_client *c) 18947d459937SAndrzej Hajda { 18957d459937SAndrzej Hajda struct v4l2_subdev *sd; 18967d459937SAndrzej Hajda int ret; 18977d459937SAndrzej Hajda 18987d459937SAndrzej Hajda sd = &state->cis_sd; 18997d459937SAndrzej Hajda v4l2_subdev_init(sd, &s5k5baf_cis_subdev_ops); 19007d459937SAndrzej Hajda sd->owner = THIS_MODULE; 19017d459937SAndrzej Hajda v4l2_set_subdevdata(sd, state); 19027d459937SAndrzej Hajda snprintf(sd->name, sizeof(sd->name), "S5K5BAF-CIS %d-%04x", 19037d459937SAndrzej Hajda i2c_adapter_id(c->adapter), c->addr); 19047d459937SAndrzej Hajda 19057d459937SAndrzej Hajda sd->internal_ops = &s5k5baf_cis_subdev_internal_ops; 19067d459937SAndrzej Hajda sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; 19077d459937SAndrzej Hajda 19087d459937SAndrzej Hajda state->cis_pad.flags = MEDIA_PAD_FL_SOURCE; 19094ca72efaSMauro Carvalho Chehab sd->entity.function = MEDIA_ENT_F_CAM_SENSOR; 1910ab22e77cSMauro Carvalho Chehab ret = media_entity_pads_init(&sd->entity, NUM_CIS_PADS, &state->cis_pad); 19117d459937SAndrzej Hajda if (ret < 0) 19127d459937SAndrzej Hajda goto err; 19137d459937SAndrzej Hajda 19147d459937SAndrzej Hajda sd = &state->sd; 19157d459937SAndrzej Hajda v4l2_i2c_subdev_init(sd, c, &s5k5baf_subdev_ops); 19167d459937SAndrzej Hajda snprintf(sd->name, sizeof(sd->name), "S5K5BAF-ISP %d-%04x", 19177d459937SAndrzej Hajda i2c_adapter_id(c->adapter), c->addr); 19187d459937SAndrzej Hajda 19197d459937SAndrzej Hajda sd->internal_ops = &s5k5baf_subdev_internal_ops; 19207d459937SAndrzej Hajda sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; 19217d459937SAndrzej Hajda 19227d459937SAndrzej Hajda state->pads[PAD_CIS].flags = MEDIA_PAD_FL_SINK; 19237d459937SAndrzej Hajda state->pads[PAD_OUT].flags = MEDIA_PAD_FL_SOURCE; 19244ca72efaSMauro Carvalho Chehab sd->entity.function = MEDIA_ENT_F_V4L2_SUBDEV_UNKNOWN; 1925ab22e77cSMauro Carvalho Chehab ret = media_entity_pads_init(&sd->entity, NUM_ISP_PADS, state->pads); 19267d459937SAndrzej Hajda 19277d459937SAndrzej Hajda if (!ret) 19287d459937SAndrzej Hajda return 0; 19297d459937SAndrzej Hajda 19307d459937SAndrzej Hajda media_entity_cleanup(&state->cis_sd.entity); 19317d459937SAndrzej Hajda err: 19327d459937SAndrzej Hajda dev_err(&c->dev, "cannot init media entity %s\n", sd->name); 19337d459937SAndrzej Hajda return ret; 19347d459937SAndrzej Hajda } 19357d459937SAndrzej Hajda 19367d459937SAndrzej Hajda static int s5k5baf_configure_regulators(struct s5k5baf *state) 19377d459937SAndrzej Hajda { 19387d459937SAndrzej Hajda struct i2c_client *c = v4l2_get_subdevdata(&state->sd); 19397d459937SAndrzej Hajda int ret; 19407d459937SAndrzej Hajda int i; 19417d459937SAndrzej Hajda 19427d459937SAndrzej Hajda for (i = 0; i < S5K5BAF_NUM_SUPPLIES; i++) 19437d459937SAndrzej Hajda state->supplies[i].supply = s5k5baf_supply_names[i]; 19447d459937SAndrzej Hajda 19457d459937SAndrzej Hajda ret = devm_regulator_bulk_get(&c->dev, S5K5BAF_NUM_SUPPLIES, 19467d459937SAndrzej Hajda state->supplies); 19477d459937SAndrzej Hajda if (ret < 0) 19487d459937SAndrzej Hajda v4l2_err(c, "failed to get regulators\n"); 19497d459937SAndrzej Hajda return ret; 19507d459937SAndrzej Hajda } 19517d459937SAndrzej Hajda 19527d459937SAndrzej Hajda static int s5k5baf_probe(struct i2c_client *c, 19537d459937SAndrzej Hajda const struct i2c_device_id *id) 19547d459937SAndrzej Hajda { 19557d459937SAndrzej Hajda struct s5k5baf *state; 19567d459937SAndrzej Hajda int ret; 19577d459937SAndrzej Hajda 19587d459937SAndrzej Hajda state = devm_kzalloc(&c->dev, sizeof(*state), GFP_KERNEL); 19597d459937SAndrzej Hajda if (!state) 19607d459937SAndrzej Hajda return -ENOMEM; 19617d459937SAndrzej Hajda 19627d459937SAndrzej Hajda mutex_init(&state->lock); 19637d459937SAndrzej Hajda state->crop_sink = s5k5baf_cis_rect; 19647d459937SAndrzej Hajda state->compose = s5k5baf_cis_rect; 19657d459937SAndrzej Hajda state->crop_source = s5k5baf_cis_rect; 19667d459937SAndrzej Hajda 19677d459937SAndrzej Hajda ret = s5k5baf_parse_device_node(state, &c->dev); 19687d459937SAndrzej Hajda if (ret < 0) 19697d459937SAndrzej Hajda return ret; 19707d459937SAndrzej Hajda 19717d459937SAndrzej Hajda ret = s5k5baf_configure_subdevs(state, c); 19727d459937SAndrzej Hajda if (ret < 0) 19737d459937SAndrzej Hajda return ret; 19747d459937SAndrzej Hajda 19757d459937SAndrzej Hajda ret = s5k5baf_configure_gpios(state); 19767d459937SAndrzej Hajda if (ret < 0) 19777d459937SAndrzej Hajda goto err_me; 19787d459937SAndrzej Hajda 19797d459937SAndrzej Hajda ret = s5k5baf_configure_regulators(state); 19807d459937SAndrzej Hajda if (ret < 0) 19817d459937SAndrzej Hajda goto err_me; 19827d459937SAndrzej Hajda 19837d459937SAndrzej Hajda state->clock = devm_clk_get(state->sd.dev, S5K5BAF_CLK_NAME); 19847d459937SAndrzej Hajda if (IS_ERR(state->clock)) { 19857d459937SAndrzej Hajda ret = -EPROBE_DEFER; 19867d459937SAndrzej Hajda goto err_me; 19877d459937SAndrzej Hajda } 19887d459937SAndrzej Hajda 19897d459937SAndrzej Hajda ret = s5k5baf_power_on(state); 19907d459937SAndrzej Hajda if (ret < 0) { 19917d459937SAndrzej Hajda ret = -EPROBE_DEFER; 19927d459937SAndrzej Hajda goto err_me; 19937d459937SAndrzej Hajda } 19947d459937SAndrzej Hajda s5k5baf_hw_init(state); 19957d459937SAndrzej Hajda ret = s5k5baf_check_fw_revision(state); 19967d459937SAndrzej Hajda 19977d459937SAndrzej Hajda s5k5baf_power_off(state); 19987d459937SAndrzej Hajda if (ret < 0) 19997d459937SAndrzej Hajda goto err_me; 20007d459937SAndrzej Hajda 20017d459937SAndrzej Hajda ret = s5k5baf_initialize_ctrls(state); 20027d459937SAndrzej Hajda if (ret < 0) 20037d459937SAndrzej Hajda goto err_me; 20047d459937SAndrzej Hajda 20057d459937SAndrzej Hajda ret = v4l2_async_register_subdev(&state->sd); 20067d459937SAndrzej Hajda if (ret < 0) 20077d459937SAndrzej Hajda goto err_ctrl; 20087d459937SAndrzej Hajda 20097d459937SAndrzej Hajda return 0; 20107d459937SAndrzej Hajda 20117d459937SAndrzej Hajda err_ctrl: 20127d459937SAndrzej Hajda v4l2_ctrl_handler_free(state->sd.ctrl_handler); 20137d459937SAndrzej Hajda err_me: 20147d459937SAndrzej Hajda media_entity_cleanup(&state->sd.entity); 20157d459937SAndrzej Hajda media_entity_cleanup(&state->cis_sd.entity); 20167d459937SAndrzej Hajda return ret; 20177d459937SAndrzej Hajda } 20187d459937SAndrzej Hajda 20197d459937SAndrzej Hajda static int s5k5baf_remove(struct i2c_client *c) 20207d459937SAndrzej Hajda { 20217d459937SAndrzej Hajda struct v4l2_subdev *sd = i2c_get_clientdata(c); 20227d459937SAndrzej Hajda struct s5k5baf *state = to_s5k5baf(sd); 20237d459937SAndrzej Hajda 20247d459937SAndrzej Hajda v4l2_async_unregister_subdev(sd); 20257d459937SAndrzej Hajda v4l2_ctrl_handler_free(sd->ctrl_handler); 20267d459937SAndrzej Hajda media_entity_cleanup(&sd->entity); 20277d459937SAndrzej Hajda 20287d459937SAndrzej Hajda sd = &state->cis_sd; 20297d459937SAndrzej Hajda v4l2_device_unregister_subdev(sd); 20307d459937SAndrzej Hajda media_entity_cleanup(&sd->entity); 20317d459937SAndrzej Hajda 20327d459937SAndrzej Hajda return 0; 20337d459937SAndrzej Hajda } 20347d459937SAndrzej Hajda 20357d459937SAndrzej Hajda static const struct i2c_device_id s5k5baf_id[] = { 20367d459937SAndrzej Hajda { S5K5BAF_DRIVER_NAME, 0 }, 20377d459937SAndrzej Hajda { }, 20387d459937SAndrzej Hajda }; 20397d459937SAndrzej Hajda MODULE_DEVICE_TABLE(i2c, s5k5baf_id); 20407d459937SAndrzej Hajda 20417d459937SAndrzej Hajda static const struct of_device_id s5k5baf_of_match[] = { 20427d459937SAndrzej Hajda { .compatible = "samsung,s5k5baf" }, 20437d459937SAndrzej Hajda { } 20447d459937SAndrzej Hajda }; 20457d459937SAndrzej Hajda MODULE_DEVICE_TABLE(of, s5k5baf_of_match); 20467d459937SAndrzej Hajda 20477d459937SAndrzej Hajda static struct i2c_driver s5k5baf_i2c_driver = { 20487d459937SAndrzej Hajda .driver = { 20497d459937SAndrzej Hajda .of_match_table = s5k5baf_of_match, 20507d459937SAndrzej Hajda .name = S5K5BAF_DRIVER_NAME 20517d459937SAndrzej Hajda }, 20527d459937SAndrzej Hajda .probe = s5k5baf_probe, 20537d459937SAndrzej Hajda .remove = s5k5baf_remove, 20547d459937SAndrzej Hajda .id_table = s5k5baf_id, 20557d459937SAndrzej Hajda }; 20567d459937SAndrzej Hajda 20577d459937SAndrzej Hajda module_i2c_driver(s5k5baf_i2c_driver); 20587d459937SAndrzej Hajda 20597d459937SAndrzej Hajda MODULE_DESCRIPTION("Samsung S5K5BAF(X) UXGA camera driver"); 20607d459937SAndrzej Hajda MODULE_AUTHOR("Andrzej Hajda <a.hajda@samsung.com>"); 20617d459937SAndrzej Hajda MODULE_LICENSE("GPL v2"); 2062