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> 337d459937SAndrzej Hajda #include <media/v4l2-of.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); 3777d459937SAndrzej Hajda 3787d459937SAndrzej Hajda for (i = 0; i < count; ++i) 3797d459937SAndrzej Hajda d[i] = le16_to_cpu(data[i]); 3807d459937SAndrzej Hajda 3817d459937SAndrzej Hajda f = (struct s5k5baf_fw *)d; 3827d459937SAndrzej Hajda if (count < 1 + 2 * f->count) { 383383cc04cSSachin Kamat dev_err(dev, "invalid firmware header (count=%d size=%zu)\n", 3847d459937SAndrzej Hajda f->count, 2 * (count + S5K5BAG_FW_TAG_LEN)); 3857d459937SAndrzej Hajda return -EINVAL; 3867d459937SAndrzej Hajda } 3877d459937SAndrzej Hajda end = d + count; 3887d459937SAndrzej Hajda d += 1 + 2 * f->count; 3897d459937SAndrzej Hajda 3907d459937SAndrzej Hajda for (i = 0; i < f->count; ++i) { 3917d459937SAndrzej Hajda if (f->seq[i].offset + d <= end) 3927d459937SAndrzej Hajda continue; 3937d459937SAndrzej Hajda dev_err(dev, "invalid firmware header (seq=%d)\n", i); 3947d459937SAndrzej Hajda return -EINVAL; 3957d459937SAndrzej Hajda } 3967d459937SAndrzej Hajda 3977d459937SAndrzej Hajda *fw = f; 3987d459937SAndrzej Hajda 3997d459937SAndrzej Hajda return 0; 4007d459937SAndrzej Hajda } 4017d459937SAndrzej Hajda 4027d459937SAndrzej Hajda static inline struct v4l2_subdev *ctrl_to_sd(struct v4l2_ctrl *ctrl) 4037d459937SAndrzej Hajda { 4047d459937SAndrzej Hajda return &container_of(ctrl->handler, struct s5k5baf, ctrls.handler)->sd; 4057d459937SAndrzej Hajda } 4067d459937SAndrzej Hajda 4077d459937SAndrzej Hajda static inline bool s5k5baf_is_cis_subdev(struct v4l2_subdev *sd) 4087d459937SAndrzej Hajda { 4097d459937SAndrzej Hajda return sd->entity.type == MEDIA_ENT_T_V4L2_SUBDEV_SENSOR; 4107d459937SAndrzej Hajda } 4117d459937SAndrzej Hajda 4127d459937SAndrzej Hajda static inline struct s5k5baf *to_s5k5baf(struct v4l2_subdev *sd) 4137d459937SAndrzej Hajda { 4147d459937SAndrzej Hajda if (s5k5baf_is_cis_subdev(sd)) 4157d459937SAndrzej Hajda return container_of(sd, struct s5k5baf, cis_sd); 4167d459937SAndrzej Hajda else 4177d459937SAndrzej Hajda return container_of(sd, struct s5k5baf, sd); 4187d459937SAndrzej Hajda } 4197d459937SAndrzej Hajda 4207d459937SAndrzej Hajda static u16 s5k5baf_i2c_read(struct s5k5baf *state, u16 addr) 4217d459937SAndrzej Hajda { 4227d459937SAndrzej Hajda struct i2c_client *c = v4l2_get_subdevdata(&state->sd); 4237d459937SAndrzej Hajda __be16 w, r; 4240348bb1aSHans Verkuil u16 res; 4257d459937SAndrzej Hajda struct i2c_msg msg[] = { 4267d459937SAndrzej Hajda { .addr = c->addr, .flags = 0, 4277d459937SAndrzej Hajda .len = 2, .buf = (u8 *)&w }, 4287d459937SAndrzej Hajda { .addr = c->addr, .flags = I2C_M_RD, 4297d459937SAndrzej Hajda .len = 2, .buf = (u8 *)&r }, 4307d459937SAndrzej Hajda }; 4317d459937SAndrzej Hajda int ret; 4327d459937SAndrzej Hajda 4337d459937SAndrzej Hajda if (state->error) 4347d459937SAndrzej Hajda return 0; 4357d459937SAndrzej Hajda 4367d459937SAndrzej Hajda w = cpu_to_be16(addr); 4377d459937SAndrzej Hajda ret = i2c_transfer(c->adapter, msg, 2); 4380348bb1aSHans Verkuil res = be16_to_cpu(r); 4397d459937SAndrzej Hajda 4400348bb1aSHans Verkuil v4l2_dbg(3, debug, c, "i2c_read: 0x%04x : 0x%04x\n", addr, res); 4417d459937SAndrzej Hajda 4427d459937SAndrzej Hajda if (ret != 2) { 4437d459937SAndrzej Hajda v4l2_err(c, "i2c_read: error during transfer (%d)\n", ret); 4447d459937SAndrzej Hajda state->error = ret; 4457d459937SAndrzej Hajda } 4460348bb1aSHans Verkuil return res; 4477d459937SAndrzej Hajda } 4487d459937SAndrzej Hajda 4497d459937SAndrzej Hajda static void s5k5baf_i2c_write(struct s5k5baf *state, u16 addr, u16 val) 4507d459937SAndrzej Hajda { 4517d459937SAndrzej Hajda u8 buf[4] = { addr >> 8, addr & 0xFF, val >> 8, val & 0xFF }; 4527d459937SAndrzej Hajda struct i2c_client *c = v4l2_get_subdevdata(&state->sd); 4537d459937SAndrzej Hajda int ret; 4547d459937SAndrzej Hajda 4557d459937SAndrzej Hajda if (state->error) 4567d459937SAndrzej Hajda return; 4577d459937SAndrzej Hajda 4587d459937SAndrzej Hajda ret = i2c_master_send(c, buf, 4); 4597d459937SAndrzej Hajda v4l2_dbg(3, debug, c, "i2c_write: 0x%04x : 0x%04x\n", addr, val); 4607d459937SAndrzej Hajda 4617d459937SAndrzej Hajda if (ret != 4) { 4627d459937SAndrzej Hajda v4l2_err(c, "i2c_write: error during transfer (%d)\n", ret); 4637d459937SAndrzej Hajda state->error = ret; 4647d459937SAndrzej Hajda } 4657d459937SAndrzej Hajda } 4667d459937SAndrzej Hajda 4677d459937SAndrzej Hajda static u16 s5k5baf_read(struct s5k5baf *state, u16 addr) 4687d459937SAndrzej Hajda { 4697d459937SAndrzej Hajda s5k5baf_i2c_write(state, REG_CMDRD_ADDR, addr); 4707d459937SAndrzej Hajda return s5k5baf_i2c_read(state, REG_CMD_BUF); 4717d459937SAndrzej Hajda } 4727d459937SAndrzej Hajda 4737d459937SAndrzej Hajda static void s5k5baf_write(struct s5k5baf *state, u16 addr, u16 val) 4747d459937SAndrzej Hajda { 4757d459937SAndrzej Hajda s5k5baf_i2c_write(state, REG_CMDWR_ADDR, addr); 4767d459937SAndrzej Hajda s5k5baf_i2c_write(state, REG_CMD_BUF, val); 4777d459937SAndrzej Hajda } 4787d459937SAndrzej Hajda 4797d459937SAndrzej Hajda static void s5k5baf_write_arr_seq(struct s5k5baf *state, u16 addr, 4807d459937SAndrzej Hajda u16 count, const u16 *seq) 4817d459937SAndrzej Hajda { 4827d459937SAndrzej Hajda struct i2c_client *c = v4l2_get_subdevdata(&state->sd); 4837e8f15c5SAndrzej Hajda __be16 buf[65]; 4847d459937SAndrzej Hajda 4857d459937SAndrzej Hajda s5k5baf_i2c_write(state, REG_CMDWR_ADDR, addr); 4867d459937SAndrzej Hajda if (state->error) 4877d459937SAndrzej Hajda return; 4887d459937SAndrzej Hajda 4897d459937SAndrzej Hajda v4l2_dbg(3, debug, c, "i2c_write_seq(count=%d): %*ph\n", count, 4907e8f15c5SAndrzej Hajda min(2 * count, 64), seq); 4917d459937SAndrzej Hajda 4927e8f15c5SAndrzej Hajda buf[0] = __constant_cpu_to_be16(REG_CMD_BUF); 4937e8f15c5SAndrzej Hajda 4947e8f15c5SAndrzej Hajda while (count > 0) { 4957e8f15c5SAndrzej Hajda int n = min_t(int, count, ARRAY_SIZE(buf) - 1); 4967e8f15c5SAndrzej Hajda int ret, i; 4977e8f15c5SAndrzej Hajda 4987e8f15c5SAndrzej Hajda for (i = 1; i <= n; ++i) 4997e8f15c5SAndrzej Hajda buf[i] = cpu_to_be16(*seq++); 5007e8f15c5SAndrzej Hajda 5017e8f15c5SAndrzej Hajda i *= 2; 5027e8f15c5SAndrzej Hajda ret = i2c_master_send(c, (char *)buf, i); 5037e8f15c5SAndrzej Hajda if (ret != i) { 5047d459937SAndrzej Hajda v4l2_err(c, "i2c_write_seq: error during transfer (%d)\n", ret); 5057d459937SAndrzej Hajda state->error = ret; 5067e8f15c5SAndrzej Hajda break; 5077e8f15c5SAndrzej Hajda } 5087e8f15c5SAndrzej Hajda 5097e8f15c5SAndrzej Hajda count -= n; 5107d459937SAndrzej Hajda } 5117d459937SAndrzej Hajda } 5127d459937SAndrzej Hajda 5137d459937SAndrzej Hajda #define s5k5baf_write_seq(state, addr, seq...) \ 5147d459937SAndrzej Hajda s5k5baf_write_arr_seq(state, addr, sizeof((char[]){ seq }), \ 5157d459937SAndrzej Hajda (const u16 []){ seq }); 5167d459937SAndrzej Hajda 5177d459937SAndrzej Hajda /* add items count at the beginning of the list */ 5187d459937SAndrzej Hajda #define NSEQ(seq...) sizeof((char[]){ seq }), seq 5197d459937SAndrzej Hajda 5207d459937SAndrzej Hajda /* 5217d459937SAndrzej Hajda * s5k5baf_write_nseq() - Writes sequences of values to sensor memory via i2c 5227d459937SAndrzej Hajda * @nseq: sequence of u16 words in format: 5237d459937SAndrzej Hajda * (N, address, value[1]...value[N-1])*,0 5247d459937SAndrzej Hajda * Ex.: 5257d459937SAndrzej Hajda * u16 seq[] = { NSEQ(0x4000, 1, 1), NSEQ(0x4010, 640, 480), 0 }; 5267d459937SAndrzej Hajda * ret = s5k5baf_write_nseq(c, seq); 5277d459937SAndrzej Hajda */ 5287d459937SAndrzej Hajda static void s5k5baf_write_nseq(struct s5k5baf *state, const u16 *nseq) 5297d459937SAndrzej Hajda { 5307d459937SAndrzej Hajda int count; 5317d459937SAndrzej Hajda 5327d459937SAndrzej Hajda while ((count = *nseq++)) { 5337d459937SAndrzej Hajda u16 addr = *nseq++; 5347d459937SAndrzej Hajda --count; 5357d459937SAndrzej Hajda 5367d459937SAndrzej Hajda s5k5baf_write_arr_seq(state, addr, count, nseq); 5377d459937SAndrzej Hajda nseq += count; 5387d459937SAndrzej Hajda } 5397d459937SAndrzej Hajda } 5407d459937SAndrzej Hajda 5417d459937SAndrzej Hajda static void s5k5baf_synchronize(struct s5k5baf *state, int timeout, u16 addr) 5427d459937SAndrzej Hajda { 5437d459937SAndrzej Hajda unsigned long end = jiffies + msecs_to_jiffies(timeout); 5447d459937SAndrzej Hajda u16 reg; 5457d459937SAndrzej Hajda 5467d459937SAndrzej Hajda s5k5baf_write(state, addr, 1); 5477d459937SAndrzej Hajda do { 5487d459937SAndrzej Hajda reg = s5k5baf_read(state, addr); 5497d459937SAndrzej Hajda if (state->error || !reg) 5507d459937SAndrzej Hajda return; 5517d459937SAndrzej Hajda usleep_range(5000, 10000); 5527d459937SAndrzej Hajda } while (time_is_after_jiffies(end)); 5537d459937SAndrzej Hajda 5547d459937SAndrzej Hajda v4l2_err(&state->sd, "timeout on register synchronize (%#x)\n", addr); 5557d459937SAndrzej Hajda state->error = -ETIMEDOUT; 5567d459937SAndrzej Hajda } 5577d459937SAndrzej Hajda 5587d459937SAndrzej Hajda static u16 *s5k5baf_fw_get_seq(struct s5k5baf *state, u16 seq_id) 5597d459937SAndrzej Hajda { 5607d459937SAndrzej Hajda struct s5k5baf_fw *fw = state->fw; 561c0ee6273SSachin Kamat u16 *data; 5627d459937SAndrzej Hajda int i; 5637d459937SAndrzej Hajda 5647d459937SAndrzej Hajda if (fw == NULL) 5657d459937SAndrzej Hajda return NULL; 5667d459937SAndrzej Hajda 567c0ee6273SSachin Kamat data = fw->data + 2 * fw->count; 568c0ee6273SSachin Kamat 5697d459937SAndrzej Hajda for (i = 0; i < fw->count; ++i) { 5707d459937SAndrzej Hajda if (fw->seq[i].id == seq_id) 5717d459937SAndrzej Hajda return data + fw->seq[i].offset; 5727d459937SAndrzej Hajda } 5737d459937SAndrzej Hajda 5747d459937SAndrzej Hajda return NULL; 5757d459937SAndrzej Hajda } 5767d459937SAndrzej Hajda 5777d459937SAndrzej Hajda static void s5k5baf_hw_patch(struct s5k5baf *state) 5787d459937SAndrzej Hajda { 5797d459937SAndrzej Hajda u16 *seq = s5k5baf_fw_get_seq(state, S5K5BAF_FW_ID_PATCH); 5807d459937SAndrzej Hajda 5817d459937SAndrzej Hajda if (seq) 5827d459937SAndrzej Hajda s5k5baf_write_nseq(state, seq); 5837d459937SAndrzej Hajda } 5847d459937SAndrzej Hajda 5857d459937SAndrzej Hajda static void s5k5baf_hw_set_clocks(struct s5k5baf *state) 5867d459937SAndrzej Hajda { 5877d459937SAndrzej Hajda unsigned long mclk = state->mclk_frequency / 1000; 5887d459937SAndrzej Hajda u16 status; 5897d459937SAndrzej Hajda static const u16 nseq_clk_cfg[] = { 5907d459937SAndrzej Hajda NSEQ(REG_I_USE_NPVI_CLOCKS, 5917d459937SAndrzej Hajda NPVI_CLOCKS, NMIPI_CLOCKS, 0, 5927d459937SAndrzej Hajda SCLK_PVI_FREQ / 4, PCLK_MIN_FREQ / 4, PCLK_MAX_FREQ / 4, 5937d459937SAndrzej Hajda SCLK_MIPI_FREQ / 4, PCLK_MIN_FREQ / 4, PCLK_MAX_FREQ / 4), 5947d459937SAndrzej Hajda NSEQ(REG_I_USE_REGS_API, 1), 5957d459937SAndrzej Hajda 0 5967d459937SAndrzej Hajda }; 5977d459937SAndrzej Hajda 5987d459937SAndrzej Hajda s5k5baf_write_seq(state, REG_I_INCLK_FREQ_L, mclk & 0xffff, mclk >> 16); 5997d459937SAndrzej Hajda s5k5baf_write_nseq(state, nseq_clk_cfg); 6007d459937SAndrzej Hajda 6017d459937SAndrzej Hajda s5k5baf_synchronize(state, 250, REG_I_INIT_PARAMS_UPDATED); 6027d459937SAndrzej Hajda status = s5k5baf_read(state, REG_I_ERROR_INFO); 6037d459937SAndrzej Hajda if (!state->error && status) { 6047d459937SAndrzej Hajda v4l2_err(&state->sd, "error configuring PLL (%d)\n", status); 6057d459937SAndrzej Hajda state->error = -EINVAL; 6067d459937SAndrzej Hajda } 6077d459937SAndrzej Hajda } 6087d459937SAndrzej Hajda 6097d459937SAndrzej Hajda /* set custom color correction matrices for various illuminations */ 6107d459937SAndrzej Hajda static void s5k5baf_hw_set_ccm(struct s5k5baf *state) 6117d459937SAndrzej Hajda { 6127d459937SAndrzej Hajda u16 *seq = s5k5baf_fw_get_seq(state, S5K5BAF_FW_ID_CCM); 6137d459937SAndrzej Hajda 6147d459937SAndrzej Hajda if (seq) 6157d459937SAndrzej Hajda s5k5baf_write_nseq(state, seq); 6167d459937SAndrzej Hajda } 6177d459937SAndrzej Hajda 6187d459937SAndrzej Hajda /* CIS sensor tuning, based on undocumented android driver code */ 6197d459937SAndrzej Hajda static void s5k5baf_hw_set_cis(struct s5k5baf *state) 6207d459937SAndrzej Hajda { 6217d459937SAndrzej Hajda u16 *seq = s5k5baf_fw_get_seq(state, S5K5BAF_FW_ID_CIS); 6227d459937SAndrzej Hajda 6237d459937SAndrzej Hajda if (!seq) 6247d459937SAndrzej Hajda return; 6257d459937SAndrzej Hajda 6267d459937SAndrzej Hajda s5k5baf_i2c_write(state, REG_CMDWR_PAGE, PAGE_IF_HW); 6277d459937SAndrzej Hajda s5k5baf_write_nseq(state, seq); 6287d459937SAndrzej Hajda s5k5baf_i2c_write(state, REG_CMDWR_PAGE, PAGE_IF_SW); 6297d459937SAndrzej Hajda } 6307d459937SAndrzej Hajda 6317d459937SAndrzej Hajda static void s5k5baf_hw_sync_cfg(struct s5k5baf *state) 6327d459937SAndrzej Hajda { 6337d459937SAndrzej Hajda s5k5baf_write(state, REG_G_PREV_CFG_CHG, 1); 6347d459937SAndrzej Hajda if (state->apply_crop) { 6357d459937SAndrzej Hajda s5k5baf_write(state, REG_G_INPUTS_CHANGE_REQ, 1); 6367d459937SAndrzej Hajda s5k5baf_write(state, REG_G_PREV_CFG_BYPASS_CHANGED, 1); 6377d459937SAndrzej Hajda } 6387d459937SAndrzej Hajda s5k5baf_synchronize(state, 500, REG_G_NEW_CFG_SYNC); 6397d459937SAndrzej Hajda } 6407d459937SAndrzej Hajda /* Set horizontal and vertical image flipping */ 6417d459937SAndrzej Hajda static void s5k5baf_hw_set_mirror(struct s5k5baf *state) 6427d459937SAndrzej Hajda { 6437d459937SAndrzej Hajda u16 flip = state->ctrls.vflip->val | (state->ctrls.vflip->val << 1); 6447d459937SAndrzej Hajda 6457d459937SAndrzej Hajda s5k5baf_write(state, REG_P_PREV_MIRROR(0), flip); 6467d459937SAndrzej Hajda if (state->streaming) 6477d459937SAndrzej Hajda s5k5baf_hw_sync_cfg(state); 6487d459937SAndrzej Hajda } 6497d459937SAndrzej Hajda 6507d459937SAndrzej Hajda static void s5k5baf_hw_set_alg(struct s5k5baf *state, u16 alg, bool enable) 6517d459937SAndrzej Hajda { 6527d459937SAndrzej Hajda u16 cur_alg, new_alg; 6537d459937SAndrzej Hajda 6547d459937SAndrzej Hajda if (!state->valid_auto_alg) 6557d459937SAndrzej Hajda cur_alg = s5k5baf_read(state, REG_DBG_AUTOALG_EN); 6567d459937SAndrzej Hajda else 6577d459937SAndrzej Hajda cur_alg = state->auto_alg; 6587d459937SAndrzej Hajda 6597d459937SAndrzej Hajda new_alg = enable ? (cur_alg | alg) : (cur_alg & ~alg); 6607d459937SAndrzej Hajda 6617d459937SAndrzej Hajda if (new_alg != cur_alg) 6627d459937SAndrzej Hajda s5k5baf_write(state, REG_DBG_AUTOALG_EN, new_alg); 6637d459937SAndrzej Hajda 6647d459937SAndrzej Hajda if (state->error) 6657d459937SAndrzej Hajda return; 6667d459937SAndrzej Hajda 6677d459937SAndrzej Hajda state->valid_auto_alg = 1; 6687d459937SAndrzej Hajda state->auto_alg = new_alg; 6697d459937SAndrzej Hajda } 6707d459937SAndrzej Hajda 6717d459937SAndrzej Hajda /* Configure auto/manual white balance and R/G/B gains */ 6727d459937SAndrzej Hajda static void s5k5baf_hw_set_awb(struct s5k5baf *state, int awb) 6737d459937SAndrzej Hajda { 6747d459937SAndrzej Hajda struct s5k5baf_ctrls *ctrls = &state->ctrls; 6757d459937SAndrzej Hajda 6767d459937SAndrzej Hajda if (!awb) 6777d459937SAndrzej Hajda s5k5baf_write_seq(state, REG_SF_RGAIN, 6787d459937SAndrzej Hajda ctrls->gain_red->val, 1, 6797d459937SAndrzej Hajda S5K5BAF_GAIN_GREEN_DEF, 1, 6807d459937SAndrzej Hajda ctrls->gain_blue->val, 1, 6817d459937SAndrzej Hajda 1); 6827d459937SAndrzej Hajda 6837d459937SAndrzej Hajda s5k5baf_hw_set_alg(state, AALG_WB_EN, awb); 6847d459937SAndrzej Hajda } 6857d459937SAndrzej Hajda 6867d459937SAndrzej Hajda /* Program FW with exposure time, 'exposure' in us units */ 6877d459937SAndrzej Hajda static void s5k5baf_hw_set_user_exposure(struct s5k5baf *state, int exposure) 6887d459937SAndrzej Hajda { 6897d459937SAndrzej Hajda unsigned int time = exposure / 10; 6907d459937SAndrzej Hajda 6917d459937SAndrzej Hajda s5k5baf_write_seq(state, REG_SF_USR_EXPOSURE_L, 6927d459937SAndrzej Hajda time & 0xffff, time >> 16, 1); 6937d459937SAndrzej Hajda } 6947d459937SAndrzej Hajda 6957d459937SAndrzej Hajda static void s5k5baf_hw_set_user_gain(struct s5k5baf *state, int gain) 6967d459937SAndrzej Hajda { 6977d459937SAndrzej Hajda s5k5baf_write_seq(state, REG_SF_USR_TOT_GAIN, gain, 1); 6987d459937SAndrzej Hajda } 6997d459937SAndrzej Hajda 7007d459937SAndrzej Hajda /* Set auto/manual exposure and total gain */ 7017d459937SAndrzej Hajda static void s5k5baf_hw_set_auto_exposure(struct s5k5baf *state, int value) 7027d459937SAndrzej Hajda { 7037d459937SAndrzej Hajda if (value == V4L2_EXPOSURE_AUTO) { 7047d459937SAndrzej Hajda s5k5baf_hw_set_alg(state, AALG_AE_EN | AALG_DIVLEI_EN, true); 7057d459937SAndrzej Hajda } else { 7067d459937SAndrzej Hajda unsigned int exp_time = state->ctrls.exposure->val; 7077d459937SAndrzej Hajda 7087d459937SAndrzej Hajda s5k5baf_hw_set_user_exposure(state, exp_time); 7097d459937SAndrzej Hajda s5k5baf_hw_set_user_gain(state, state->ctrls.gain->val); 7107d459937SAndrzej Hajda s5k5baf_hw_set_alg(state, AALG_AE_EN | AALG_DIVLEI_EN, false); 7117d459937SAndrzej Hajda } 7127d459937SAndrzej Hajda } 7137d459937SAndrzej Hajda 7147d459937SAndrzej Hajda static void s5k5baf_hw_set_anti_flicker(struct s5k5baf *state, int v) 7157d459937SAndrzej Hajda { 7167d459937SAndrzej Hajda if (v == V4L2_CID_POWER_LINE_FREQUENCY_AUTO) { 7177d459937SAndrzej Hajda s5k5baf_hw_set_alg(state, AALG_FLICKER_EN, true); 7187d459937SAndrzej Hajda } else { 7197d459937SAndrzej Hajda /* The V4L2_CID_LINE_FREQUENCY control values match 7207d459937SAndrzej Hajda * the register values */ 7217d459937SAndrzej Hajda s5k5baf_write_seq(state, REG_SF_FLICKER_QUANT, v, 1); 7227d459937SAndrzej Hajda s5k5baf_hw_set_alg(state, AALG_FLICKER_EN, false); 7237d459937SAndrzej Hajda } 7247d459937SAndrzej Hajda } 7257d459937SAndrzej Hajda 7267d459937SAndrzej Hajda static void s5k5baf_hw_set_colorfx(struct s5k5baf *state, int val) 7277d459937SAndrzej Hajda { 7287d459937SAndrzej Hajda static const u16 colorfx[] = { 7297d459937SAndrzej Hajda [V4L2_COLORFX_NONE] = 0, 7307d459937SAndrzej Hajda [V4L2_COLORFX_BW] = 1, 7317d459937SAndrzej Hajda [V4L2_COLORFX_NEGATIVE] = 2, 7327d459937SAndrzej Hajda [V4L2_COLORFX_SEPIA] = 3, 7337d459937SAndrzej Hajda [V4L2_COLORFX_SKY_BLUE] = 4, 7347d459937SAndrzej Hajda [V4L2_COLORFX_SKETCH] = 5, 7357d459937SAndrzej Hajda }; 7367d459937SAndrzej Hajda 7377d459937SAndrzej Hajda s5k5baf_write(state, REG_G_SPEC_EFFECTS, colorfx[val]); 7387d459937SAndrzej Hajda } 7397d459937SAndrzej Hajda 7407d459937SAndrzej Hajda static int s5k5baf_find_pixfmt(struct v4l2_mbus_framefmt *mf) 7417d459937SAndrzej Hajda { 7427d459937SAndrzej Hajda int i, c = -1; 7437d459937SAndrzej Hajda 7447d459937SAndrzej Hajda for (i = 0; i < ARRAY_SIZE(s5k5baf_formats); i++) { 7457d459937SAndrzej Hajda if (mf->colorspace != s5k5baf_formats[i].colorspace) 7467d459937SAndrzej Hajda continue; 7477d459937SAndrzej Hajda if (mf->code == s5k5baf_formats[i].code) 7487d459937SAndrzej Hajda return i; 7497d459937SAndrzej Hajda if (c < 0) 7507d459937SAndrzej Hajda c = i; 7517d459937SAndrzej Hajda } 7527d459937SAndrzej Hajda return (c < 0) ? 0 : c; 7537d459937SAndrzej Hajda } 7547d459937SAndrzej Hajda 7557d459937SAndrzej Hajda static int s5k5baf_clear_error(struct s5k5baf *state) 7567d459937SAndrzej Hajda { 7577d459937SAndrzej Hajda int ret = state->error; 7587d459937SAndrzej Hajda 7597d459937SAndrzej Hajda state->error = 0; 7607d459937SAndrzej Hajda return ret; 7617d459937SAndrzej Hajda } 7627d459937SAndrzej Hajda 7637d459937SAndrzej Hajda static int s5k5baf_hw_set_video_bus(struct s5k5baf *state) 7647d459937SAndrzej Hajda { 7657d459937SAndrzej Hajda u16 en_pkts; 7667d459937SAndrzej Hajda 7677d459937SAndrzej Hajda if (state->bus_type == V4L2_MBUS_CSI2) 7687d459937SAndrzej Hajda en_pkts = EN_PACKETS_CSI2; 7697d459937SAndrzej Hajda else 7707d459937SAndrzej Hajda en_pkts = 0; 7717d459937SAndrzej Hajda 7727d459937SAndrzej Hajda s5k5baf_write_seq(state, REG_OIF_EN_MIPI_LANES, 7737d459937SAndrzej Hajda state->nlanes, en_pkts, 1); 7747d459937SAndrzej Hajda 7757d459937SAndrzej Hajda return s5k5baf_clear_error(state); 7767d459937SAndrzej Hajda } 7777d459937SAndrzej Hajda 7787d459937SAndrzej Hajda static u16 s5k5baf_get_cfg_error(struct s5k5baf *state) 7797d459937SAndrzej Hajda { 7807d459937SAndrzej Hajda u16 err = s5k5baf_read(state, REG_G_PREV_CFG_ERROR); 7817d459937SAndrzej Hajda if (err) 7827d459937SAndrzej Hajda s5k5baf_write(state, REG_G_PREV_CFG_ERROR, 0); 7837d459937SAndrzej Hajda return err; 7847d459937SAndrzej Hajda } 7857d459937SAndrzej Hajda 7867d459937SAndrzej Hajda static void s5k5baf_hw_set_fiv(struct s5k5baf *state, u16 fiv) 7877d459937SAndrzej Hajda { 7887d459937SAndrzej Hajda s5k5baf_write(state, REG_P_MAX_FR_TIME(0), fiv); 7897d459937SAndrzej Hajda s5k5baf_hw_sync_cfg(state); 7907d459937SAndrzej Hajda } 7917d459937SAndrzej Hajda 7927d459937SAndrzej Hajda static void s5k5baf_hw_find_min_fiv(struct s5k5baf *state) 7937d459937SAndrzej Hajda { 7947d459937SAndrzej Hajda u16 err, fiv; 7957d459937SAndrzej Hajda int n; 7967d459937SAndrzej Hajda 7977d459937SAndrzej Hajda fiv = s5k5baf_read(state, REG_G_ACTUAL_P_FR_TIME); 7987d459937SAndrzej Hajda if (state->error) 7997d459937SAndrzej Hajda return; 8007d459937SAndrzej Hajda 8017d459937SAndrzej Hajda for (n = 5; n > 0; --n) { 8027d459937SAndrzej Hajda s5k5baf_hw_set_fiv(state, fiv); 8037d459937SAndrzej Hajda err = s5k5baf_get_cfg_error(state); 8047d459937SAndrzej Hajda if (state->error) 8057d459937SAndrzej Hajda return; 8067d459937SAndrzej Hajda switch (err) { 8077d459937SAndrzej Hajda case CFG_ERROR_RANGE: 8087d459937SAndrzej Hajda ++fiv; 8097d459937SAndrzej Hajda break; 8107d459937SAndrzej Hajda case 0: 8117d459937SAndrzej Hajda state->fiv = fiv; 8127d459937SAndrzej Hajda v4l2_info(&state->sd, 8137d459937SAndrzej Hajda "found valid frame interval: %d00us\n", fiv); 8147d459937SAndrzej Hajda return; 8157d459937SAndrzej Hajda default: 8167d459937SAndrzej Hajda v4l2_err(&state->sd, 8177d459937SAndrzej Hajda "error setting frame interval: %d\n", err); 8187d459937SAndrzej Hajda state->error = -EINVAL; 8197d459937SAndrzej Hajda } 820bc39d69aSMauro Carvalho Chehab } 8217d459937SAndrzej Hajda v4l2_err(&state->sd, "cannot find correct frame interval\n"); 8227d459937SAndrzej Hajda state->error = -ERANGE; 8237d459937SAndrzej Hajda } 8247d459937SAndrzej Hajda 8257d459937SAndrzej Hajda static void s5k5baf_hw_validate_cfg(struct s5k5baf *state) 8267d459937SAndrzej Hajda { 8277d459937SAndrzej Hajda u16 err; 8287d459937SAndrzej Hajda 8297d459937SAndrzej Hajda err = s5k5baf_get_cfg_error(state); 8307d459937SAndrzej Hajda if (state->error) 8317d459937SAndrzej Hajda return; 8327d459937SAndrzej Hajda 8337d459937SAndrzej Hajda switch (err) { 8347d459937SAndrzej Hajda case 0: 8357d459937SAndrzej Hajda state->apply_cfg = 1; 8367d459937SAndrzej Hajda return; 8377d459937SAndrzej Hajda case CFG_ERROR_RANGE: 8387d459937SAndrzej Hajda s5k5baf_hw_find_min_fiv(state); 8397d459937SAndrzej Hajda if (!state->error) 8407d459937SAndrzej Hajda state->apply_cfg = 1; 8417d459937SAndrzej Hajda return; 8427d459937SAndrzej Hajda default: 8437d459937SAndrzej Hajda v4l2_err(&state->sd, 8447d459937SAndrzej Hajda "error setting format: %d\n", err); 8457d459937SAndrzej Hajda state->error = -EINVAL; 8467d459937SAndrzej Hajda } 8477d459937SAndrzej Hajda } 8487d459937SAndrzej Hajda 8497d459937SAndrzej Hajda static void s5k5baf_rescale(struct v4l2_rect *r, const struct v4l2_rect *v, 8507d459937SAndrzej Hajda const struct v4l2_rect *n, 8517d459937SAndrzej Hajda const struct v4l2_rect *d) 8527d459937SAndrzej Hajda { 8537d459937SAndrzej Hajda r->left = v->left * n->width / d->width; 8547d459937SAndrzej Hajda r->top = v->top * n->height / d->height; 8557d459937SAndrzej Hajda r->width = v->width * n->width / d->width; 8567d459937SAndrzej Hajda r->height = v->height * n->height / d->height; 8577d459937SAndrzej Hajda } 8587d459937SAndrzej Hajda 8597d459937SAndrzej Hajda static int s5k5baf_hw_set_crop_rects(struct s5k5baf *state) 8607d459937SAndrzej Hajda { 8617d459937SAndrzej Hajda struct v4l2_rect *p, r; 8627d459937SAndrzej Hajda u16 err; 8637d459937SAndrzej Hajda int ret; 8647d459937SAndrzej Hajda 8657d459937SAndrzej Hajda p = &state->crop_sink; 8667d459937SAndrzej Hajda s5k5baf_write_seq(state, REG_G_PREVREQ_IN_WIDTH, p->width, p->height, 8677d459937SAndrzej Hajda p->left, p->top); 8687d459937SAndrzej Hajda 8697d459937SAndrzej Hajda s5k5baf_rescale(&r, &state->crop_source, &state->crop_sink, 8707d459937SAndrzej Hajda &state->compose); 8717d459937SAndrzej Hajda s5k5baf_write_seq(state, REG_G_PREVZOOM_IN_WIDTH, r.width, r.height, 8727d459937SAndrzej Hajda r.left, r.top); 8737d459937SAndrzej Hajda 8747d459937SAndrzej Hajda s5k5baf_synchronize(state, 500, REG_G_INPUTS_CHANGE_REQ); 8757d459937SAndrzej Hajda s5k5baf_synchronize(state, 500, REG_G_PREV_CFG_BYPASS_CHANGED); 8767d459937SAndrzej Hajda err = s5k5baf_get_cfg_error(state); 8777d459937SAndrzej Hajda ret = s5k5baf_clear_error(state); 8787d459937SAndrzej Hajda if (ret < 0) 8797d459937SAndrzej Hajda return ret; 8807d459937SAndrzej Hajda 8817d459937SAndrzej Hajda switch (err) { 8827d459937SAndrzej Hajda case 0: 8837d459937SAndrzej Hajda break; 8847d459937SAndrzej Hajda case CFG_ERROR_RANGE: 8857d459937SAndrzej Hajda /* retry crop with frame interval set to max */ 8867d459937SAndrzej Hajda s5k5baf_hw_set_fiv(state, S5K5BAF_MAX_FR_TIME); 8877d459937SAndrzej Hajda err = s5k5baf_get_cfg_error(state); 8887d459937SAndrzej Hajda ret = s5k5baf_clear_error(state); 8897d459937SAndrzej Hajda if (ret < 0) 8907d459937SAndrzej Hajda return ret; 8917d459937SAndrzej Hajda if (err) { 8927d459937SAndrzej Hajda v4l2_err(&state->sd, 8937d459937SAndrzej Hajda "crop error on max frame interval: %d\n", err); 8947d459937SAndrzej Hajda state->error = -EINVAL; 8957d459937SAndrzej Hajda } 8967d459937SAndrzej Hajda s5k5baf_hw_set_fiv(state, state->req_fiv); 8977d459937SAndrzej Hajda s5k5baf_hw_validate_cfg(state); 8987d459937SAndrzej Hajda break; 8997d459937SAndrzej Hajda default: 9007d459937SAndrzej Hajda v4l2_err(&state->sd, "crop error: %d\n", err); 9017d459937SAndrzej Hajda return -EINVAL; 9027d459937SAndrzej Hajda } 9037d459937SAndrzej Hajda 9047d459937SAndrzej Hajda if (!state->apply_cfg) 9057d459937SAndrzej Hajda return 0; 9067d459937SAndrzej Hajda 9077d459937SAndrzej Hajda p = &state->crop_source; 9087d459937SAndrzej Hajda s5k5baf_write_seq(state, REG_P_OUT_WIDTH(0), p->width, p->height); 9097d459937SAndrzej Hajda s5k5baf_hw_set_fiv(state, state->req_fiv); 9107d459937SAndrzej Hajda s5k5baf_hw_validate_cfg(state); 9117d459937SAndrzej Hajda 9127d459937SAndrzej Hajda return s5k5baf_clear_error(state); 9137d459937SAndrzej Hajda } 9147d459937SAndrzej Hajda 9157d459937SAndrzej Hajda static void s5k5baf_hw_set_config(struct s5k5baf *state) 9167d459937SAndrzej Hajda { 9177d459937SAndrzej Hajda u16 reg_fmt = s5k5baf_formats[state->pixfmt].reg_p_fmt; 9187d459937SAndrzej Hajda struct v4l2_rect *r = &state->crop_source; 9197d459937SAndrzej Hajda 9207d459937SAndrzej Hajda s5k5baf_write_seq(state, REG_P_OUT_WIDTH(0), 9217d459937SAndrzej Hajda r->width, r->height, reg_fmt, 9227d459937SAndrzej Hajda PCLK_MAX_FREQ >> 2, PCLK_MIN_FREQ >> 2, 9237d459937SAndrzej Hajda PVI_MASK_MIPI, CLK_MIPI_INDEX, 9247d459937SAndrzej Hajda FR_RATE_FIXED, FR_RATE_Q_DYNAMIC, 9257d459937SAndrzej Hajda state->req_fiv, S5K5BAF_MIN_FR_TIME); 9267d459937SAndrzej Hajda s5k5baf_hw_sync_cfg(state); 9277d459937SAndrzej Hajda s5k5baf_hw_validate_cfg(state); 9287d459937SAndrzej Hajda } 9297d459937SAndrzej Hajda 9307d459937SAndrzej Hajda 9317d459937SAndrzej Hajda static void s5k5baf_hw_set_test_pattern(struct s5k5baf *state, int id) 9327d459937SAndrzej Hajda { 9337d459937SAndrzej Hajda s5k5baf_i2c_write(state, REG_PATTERN_WIDTH, 800); 9347d459937SAndrzej Hajda s5k5baf_i2c_write(state, REG_PATTERN_HEIGHT, 511); 9357d459937SAndrzej Hajda s5k5baf_i2c_write(state, REG_PATTERN_PARAM, 0); 9367d459937SAndrzej Hajda s5k5baf_i2c_write(state, REG_PATTERN_SET, id); 9377d459937SAndrzej Hajda } 9387d459937SAndrzej Hajda 9397d459937SAndrzej Hajda static void s5k5baf_gpio_assert(struct s5k5baf *state, int id) 9407d459937SAndrzej Hajda { 9417d459937SAndrzej Hajda struct s5k5baf_gpio *gpio = &state->gpios[id]; 9427d459937SAndrzej Hajda 9437d459937SAndrzej Hajda gpio_set_value(gpio->gpio, gpio->level); 9447d459937SAndrzej Hajda } 9457d459937SAndrzej Hajda 9467d459937SAndrzej Hajda static void s5k5baf_gpio_deassert(struct s5k5baf *state, int id) 9477d459937SAndrzej Hajda { 9487d459937SAndrzej Hajda struct s5k5baf_gpio *gpio = &state->gpios[id]; 9497d459937SAndrzej Hajda 9507d459937SAndrzej Hajda gpio_set_value(gpio->gpio, !gpio->level); 9517d459937SAndrzej Hajda } 9527d459937SAndrzej Hajda 9537d459937SAndrzej Hajda static int s5k5baf_power_on(struct s5k5baf *state) 9547d459937SAndrzej Hajda { 9557d459937SAndrzej Hajda int ret; 9567d459937SAndrzej Hajda 9577d459937SAndrzej Hajda ret = regulator_bulk_enable(S5K5BAF_NUM_SUPPLIES, state->supplies); 9587d459937SAndrzej Hajda if (ret < 0) 9597d459937SAndrzej Hajda goto err; 9607d459937SAndrzej Hajda 9617d459937SAndrzej Hajda ret = clk_set_rate(state->clock, state->mclk_frequency); 9627d459937SAndrzej Hajda if (ret < 0) 9637d459937SAndrzej Hajda goto err_reg_dis; 9647d459937SAndrzej Hajda 9657d459937SAndrzej Hajda ret = clk_prepare_enable(state->clock); 9667d459937SAndrzej Hajda if (ret < 0) 9677d459937SAndrzej Hajda goto err_reg_dis; 9687d459937SAndrzej Hajda 9697d459937SAndrzej Hajda v4l2_dbg(1, debug, &state->sd, "clock frequency: %ld\n", 9707d459937SAndrzej Hajda clk_get_rate(state->clock)); 9717d459937SAndrzej Hajda 9727d459937SAndrzej Hajda s5k5baf_gpio_deassert(state, STBY); 9737d459937SAndrzej Hajda usleep_range(50, 100); 9747d459937SAndrzej Hajda s5k5baf_gpio_deassert(state, RST); 9757d459937SAndrzej Hajda return 0; 9767d459937SAndrzej Hajda 9777d459937SAndrzej Hajda err_reg_dis: 9787d459937SAndrzej Hajda regulator_bulk_disable(S5K5BAF_NUM_SUPPLIES, state->supplies); 9797d459937SAndrzej Hajda err: 9807d459937SAndrzej Hajda v4l2_err(&state->sd, "%s() failed (%d)\n", __func__, ret); 9817d459937SAndrzej Hajda return ret; 9827d459937SAndrzej Hajda } 9837d459937SAndrzej Hajda 9847d459937SAndrzej Hajda static int s5k5baf_power_off(struct s5k5baf *state) 9857d459937SAndrzej Hajda { 9867d459937SAndrzej Hajda int ret; 9877d459937SAndrzej Hajda 9887d459937SAndrzej Hajda state->streaming = 0; 9897d459937SAndrzej Hajda state->apply_cfg = 0; 9907d459937SAndrzej Hajda state->apply_crop = 0; 9917d459937SAndrzej Hajda 9927d459937SAndrzej Hajda s5k5baf_gpio_assert(state, RST); 9937d459937SAndrzej Hajda s5k5baf_gpio_assert(state, STBY); 9947d459937SAndrzej Hajda 9957d459937SAndrzej Hajda if (!IS_ERR(state->clock)) 9967d459937SAndrzej Hajda clk_disable_unprepare(state->clock); 9977d459937SAndrzej Hajda 9987d459937SAndrzej Hajda ret = regulator_bulk_disable(S5K5BAF_NUM_SUPPLIES, 9997d459937SAndrzej Hajda state->supplies); 10007d459937SAndrzej Hajda if (ret < 0) 10017d459937SAndrzej Hajda v4l2_err(&state->sd, "failed to disable regulators\n"); 10027d459937SAndrzej Hajda 10037d459937SAndrzej Hajda return 0; 10047d459937SAndrzej Hajda } 10057d459937SAndrzej Hajda 10067d459937SAndrzej Hajda static void s5k5baf_hw_init(struct s5k5baf *state) 10077d459937SAndrzej Hajda { 10087d459937SAndrzej Hajda s5k5baf_i2c_write(state, AHB_MSB_ADDR_PTR, PAGE_IF_HW); 10097d459937SAndrzej Hajda s5k5baf_i2c_write(state, REG_CLEAR_HOST_INT, 0); 10107d459937SAndrzej Hajda s5k5baf_i2c_write(state, REG_SW_LOAD_COMPLETE, 1); 10117d459937SAndrzej Hajda s5k5baf_i2c_write(state, REG_CMDRD_PAGE, PAGE_IF_SW); 10127d459937SAndrzej Hajda s5k5baf_i2c_write(state, REG_CMDWR_PAGE, PAGE_IF_SW); 10137d459937SAndrzej Hajda } 10147d459937SAndrzej Hajda 10157d459937SAndrzej Hajda /* 10167d459937SAndrzej Hajda * V4L2 subdev core and video operations 10177d459937SAndrzej Hajda */ 10187d459937SAndrzej Hajda 10197d459937SAndrzej Hajda static void s5k5baf_initialize_data(struct s5k5baf *state) 10207d459937SAndrzej Hajda { 10217d459937SAndrzej Hajda state->pixfmt = 0; 10227d459937SAndrzej Hajda state->req_fiv = 10000 / 15; 10237d459937SAndrzej Hajda state->fiv = state->req_fiv; 10247d459937SAndrzej Hajda state->valid_auto_alg = 0; 10257d459937SAndrzej Hajda } 10267d459937SAndrzej Hajda 10277d459937SAndrzej Hajda static int s5k5baf_load_setfile(struct s5k5baf *state) 10287d459937SAndrzej Hajda { 10297d459937SAndrzej Hajda struct i2c_client *c = v4l2_get_subdevdata(&state->sd); 10307d459937SAndrzej Hajda const struct firmware *fw; 10317d459937SAndrzej Hajda int ret; 10327d459937SAndrzej Hajda 10337d459937SAndrzej Hajda ret = request_firmware(&fw, S5K5BAF_FW_FILENAME, &c->dev); 10347d459937SAndrzej Hajda if (ret < 0) { 10357d459937SAndrzej Hajda dev_warn(&c->dev, "firmware file (%s) not loaded\n", 10367d459937SAndrzej Hajda S5K5BAF_FW_FILENAME); 10377d459937SAndrzej Hajda return ret; 10387d459937SAndrzej Hajda } 10397d459937SAndrzej Hajda 10407d459937SAndrzej Hajda ret = s5k5baf_fw_parse(&c->dev, &state->fw, fw->size / 2, 10410348bb1aSHans Verkuil (__le16 *)fw->data); 10427d459937SAndrzej Hajda 10437d459937SAndrzej Hajda release_firmware(fw); 10447d459937SAndrzej Hajda 10457d459937SAndrzej Hajda return ret; 10467d459937SAndrzej Hajda } 10477d459937SAndrzej Hajda 10487d459937SAndrzej Hajda static int s5k5baf_set_power(struct v4l2_subdev *sd, int on) 10497d459937SAndrzej Hajda { 10507d459937SAndrzej Hajda struct s5k5baf *state = to_s5k5baf(sd); 10517d459937SAndrzej Hajda int ret = 0; 10527d459937SAndrzej Hajda 10537d459937SAndrzej Hajda mutex_lock(&state->lock); 10547d459937SAndrzej Hajda 10557d459937SAndrzej Hajda if (!on != state->power) 10567d459937SAndrzej Hajda goto out; 10577d459937SAndrzej Hajda 10587d459937SAndrzej Hajda if (on) { 10597d459937SAndrzej Hajda if (state->fw == NULL) 10607d459937SAndrzej Hajda s5k5baf_load_setfile(state); 10617d459937SAndrzej Hajda 10627d459937SAndrzej Hajda s5k5baf_initialize_data(state); 10637d459937SAndrzej Hajda ret = s5k5baf_power_on(state); 10647d459937SAndrzej Hajda if (ret < 0) 10657d459937SAndrzej Hajda goto out; 10667d459937SAndrzej Hajda 10677d459937SAndrzej Hajda s5k5baf_hw_init(state); 10687d459937SAndrzej Hajda s5k5baf_hw_patch(state); 10697d459937SAndrzej Hajda s5k5baf_i2c_write(state, REG_SET_HOST_INT, 1); 10707d459937SAndrzej Hajda s5k5baf_hw_set_clocks(state); 10717d459937SAndrzej Hajda 10727d459937SAndrzej Hajda ret = s5k5baf_hw_set_video_bus(state); 10737d459937SAndrzej Hajda if (ret < 0) 10747d459937SAndrzej Hajda goto out; 10757d459937SAndrzej Hajda 10767d459937SAndrzej Hajda s5k5baf_hw_set_cis(state); 10777d459937SAndrzej Hajda s5k5baf_hw_set_ccm(state); 10787d459937SAndrzej Hajda 10797d459937SAndrzej Hajda ret = s5k5baf_clear_error(state); 10807d459937SAndrzej Hajda if (!ret) 10817d459937SAndrzej Hajda state->power++; 10827d459937SAndrzej Hajda } else { 10837d459937SAndrzej Hajda s5k5baf_power_off(state); 10847d459937SAndrzej Hajda state->power--; 10857d459937SAndrzej Hajda } 10867d459937SAndrzej Hajda 10877d459937SAndrzej Hajda out: 10887d459937SAndrzej Hajda mutex_unlock(&state->lock); 10897d459937SAndrzej Hajda 10907d459937SAndrzej Hajda if (!ret && on) 10917d459937SAndrzej Hajda ret = v4l2_ctrl_handler_setup(&state->ctrls.handler); 10927d459937SAndrzej Hajda 10937d459937SAndrzej Hajda return ret; 10947d459937SAndrzej Hajda } 10957d459937SAndrzej Hajda 10967d459937SAndrzej Hajda static void s5k5baf_hw_set_stream(struct s5k5baf *state, int enable) 10977d459937SAndrzej Hajda { 10987d459937SAndrzej Hajda s5k5baf_write_seq(state, REG_G_ENABLE_PREV, enable, 1); 10997d459937SAndrzej Hajda } 11007d459937SAndrzej Hajda 11017d459937SAndrzej Hajda static int s5k5baf_s_stream(struct v4l2_subdev *sd, int on) 11027d459937SAndrzej Hajda { 11037d459937SAndrzej Hajda struct s5k5baf *state = to_s5k5baf(sd); 11047d459937SAndrzej Hajda int ret; 11057d459937SAndrzej Hajda 11067d459937SAndrzej Hajda mutex_lock(&state->lock); 11077d459937SAndrzej Hajda 11087d459937SAndrzej Hajda if (state->streaming == !!on) { 11097d459937SAndrzej Hajda ret = 0; 11107d459937SAndrzej Hajda goto out; 11117d459937SAndrzej Hajda } 11127d459937SAndrzej Hajda 11137d459937SAndrzej Hajda if (on) { 11147d459937SAndrzej Hajda s5k5baf_hw_set_config(state); 11157d459937SAndrzej Hajda ret = s5k5baf_hw_set_crop_rects(state); 11167d459937SAndrzej Hajda if (ret < 0) 11177d459937SAndrzej Hajda goto out; 11187d459937SAndrzej Hajda s5k5baf_hw_set_stream(state, 1); 11197d459937SAndrzej Hajda s5k5baf_i2c_write(state, 0xb0cc, 0x000b); 11207d459937SAndrzej Hajda } else { 11217d459937SAndrzej Hajda s5k5baf_hw_set_stream(state, 0); 11227d459937SAndrzej Hajda } 11237d459937SAndrzej Hajda ret = s5k5baf_clear_error(state); 11247d459937SAndrzej Hajda if (!ret) 11257d459937SAndrzej Hajda state->streaming = !state->streaming; 11267d459937SAndrzej Hajda 11277d459937SAndrzej Hajda out: 11287d459937SAndrzej Hajda mutex_unlock(&state->lock); 11297d459937SAndrzej Hajda 11307d459937SAndrzej Hajda return ret; 11317d459937SAndrzej Hajda } 11327d459937SAndrzej Hajda 11337d459937SAndrzej Hajda static int s5k5baf_g_frame_interval(struct v4l2_subdev *sd, 11347d459937SAndrzej Hajda struct v4l2_subdev_frame_interval *fi) 11357d459937SAndrzej Hajda { 11367d459937SAndrzej Hajda struct s5k5baf *state = to_s5k5baf(sd); 11377d459937SAndrzej Hajda 11387d459937SAndrzej Hajda mutex_lock(&state->lock); 11397d459937SAndrzej Hajda fi->interval.numerator = state->fiv; 11407d459937SAndrzej Hajda fi->interval.denominator = 10000; 11417d459937SAndrzej Hajda mutex_unlock(&state->lock); 11427d459937SAndrzej Hajda 11437d459937SAndrzej Hajda return 0; 11447d459937SAndrzej Hajda } 11457d459937SAndrzej Hajda 11467d459937SAndrzej Hajda static void s5k5baf_set_frame_interval(struct s5k5baf *state, 11477d459937SAndrzej Hajda struct v4l2_subdev_frame_interval *fi) 11487d459937SAndrzej Hajda { 11497d459937SAndrzej Hajda struct v4l2_fract *i = &fi->interval; 11507d459937SAndrzej Hajda 11517d459937SAndrzej Hajda if (fi->interval.denominator == 0) 11527d459937SAndrzej Hajda state->req_fiv = S5K5BAF_MAX_FR_TIME; 11537d459937SAndrzej Hajda else 11547d459937SAndrzej Hajda state->req_fiv = clamp_t(u32, 11557d459937SAndrzej Hajda i->numerator * 10000 / i->denominator, 11567d459937SAndrzej Hajda S5K5BAF_MIN_FR_TIME, 11577d459937SAndrzej Hajda S5K5BAF_MAX_FR_TIME); 11587d459937SAndrzej Hajda 11597d459937SAndrzej Hajda state->fiv = state->req_fiv; 11607d459937SAndrzej Hajda if (state->apply_cfg) { 11617d459937SAndrzej Hajda s5k5baf_hw_set_fiv(state, state->req_fiv); 11627d459937SAndrzej Hajda s5k5baf_hw_validate_cfg(state); 11637d459937SAndrzej Hajda } 11647d459937SAndrzej Hajda *i = (struct v4l2_fract){ state->fiv, 10000 }; 11657d459937SAndrzej Hajda if (state->fiv == state->req_fiv) 11667d459937SAndrzej Hajda v4l2_info(&state->sd, "frame interval changed to %d00us\n", 11677d459937SAndrzej Hajda state->fiv); 11687d459937SAndrzej Hajda } 11697d459937SAndrzej Hajda 11707d459937SAndrzej Hajda static int s5k5baf_s_frame_interval(struct v4l2_subdev *sd, 11717d459937SAndrzej Hajda struct v4l2_subdev_frame_interval *fi) 11727d459937SAndrzej Hajda { 11737d459937SAndrzej Hajda struct s5k5baf *state = to_s5k5baf(sd); 11747d459937SAndrzej Hajda 11757d459937SAndrzej Hajda mutex_lock(&state->lock); 11767d459937SAndrzej Hajda s5k5baf_set_frame_interval(state, fi); 11777d459937SAndrzej Hajda mutex_unlock(&state->lock); 11787d459937SAndrzej Hajda return 0; 11797d459937SAndrzej Hajda } 11807d459937SAndrzej Hajda 11817d459937SAndrzej Hajda /* 11827d459937SAndrzej Hajda * V4L2 subdev pad level and video operations 11837d459937SAndrzej Hajda */ 11847d459937SAndrzej Hajda static int s5k5baf_enum_frame_interval(struct v4l2_subdev *sd, 1185f7234138SHans Verkuil struct v4l2_subdev_pad_config *cfg, 11867d459937SAndrzej Hajda struct v4l2_subdev_frame_interval_enum *fie) 11877d459937SAndrzej Hajda { 11887d459937SAndrzej Hajda if (fie->index > S5K5BAF_MAX_FR_TIME - S5K5BAF_MIN_FR_TIME || 11897d459937SAndrzej Hajda fie->pad != PAD_CIS) 11907d459937SAndrzej Hajda return -EINVAL; 11917d459937SAndrzej Hajda 11927d459937SAndrzej Hajda v4l_bound_align_image(&fie->width, S5K5BAF_WIN_WIDTH_MIN, 11937d459937SAndrzej Hajda S5K5BAF_CIS_WIDTH, 1, 11947d459937SAndrzej Hajda &fie->height, S5K5BAF_WIN_HEIGHT_MIN, 11957d459937SAndrzej Hajda S5K5BAF_CIS_HEIGHT, 1, 0); 11967d459937SAndrzej Hajda 11977d459937SAndrzej Hajda fie->interval.numerator = S5K5BAF_MIN_FR_TIME + fie->index; 11987d459937SAndrzej Hajda fie->interval.denominator = 10000; 11997d459937SAndrzej Hajda 12007d459937SAndrzej Hajda return 0; 12017d459937SAndrzej Hajda } 12027d459937SAndrzej Hajda 12037d459937SAndrzej Hajda static int s5k5baf_enum_mbus_code(struct v4l2_subdev *sd, 1204f7234138SHans Verkuil struct v4l2_subdev_pad_config *cfg, 12057d459937SAndrzej Hajda struct v4l2_subdev_mbus_code_enum *code) 12067d459937SAndrzej Hajda { 12077d459937SAndrzej Hajda if (code->pad == PAD_CIS) { 12087d459937SAndrzej Hajda if (code->index > 0) 12097d459937SAndrzej Hajda return -EINVAL; 1210f5fe58fdSBoris BREZILLON code->code = MEDIA_BUS_FMT_FIXED; 12117d459937SAndrzej Hajda return 0; 12127d459937SAndrzej Hajda } 12137d459937SAndrzej Hajda 12147d459937SAndrzej Hajda if (code->index >= ARRAY_SIZE(s5k5baf_formats)) 12157d459937SAndrzej Hajda return -EINVAL; 12167d459937SAndrzej Hajda 12177d459937SAndrzej Hajda code->code = s5k5baf_formats[code->index].code; 12187d459937SAndrzej Hajda return 0; 12197d459937SAndrzej Hajda } 12207d459937SAndrzej Hajda 12217d459937SAndrzej Hajda static int s5k5baf_enum_frame_size(struct v4l2_subdev *sd, 1222f7234138SHans Verkuil struct v4l2_subdev_pad_config *cfg, 12237d459937SAndrzej Hajda struct v4l2_subdev_frame_size_enum *fse) 12247d459937SAndrzej Hajda { 12257d459937SAndrzej Hajda int i; 12267d459937SAndrzej Hajda 12277d459937SAndrzej Hajda if (fse->index > 0) 12287d459937SAndrzej Hajda return -EINVAL; 12297d459937SAndrzej Hajda 12307d459937SAndrzej Hajda if (fse->pad == PAD_CIS) { 1231f5fe58fdSBoris BREZILLON fse->code = MEDIA_BUS_FMT_FIXED; 12327d459937SAndrzej Hajda fse->min_width = S5K5BAF_CIS_WIDTH; 12337d459937SAndrzej Hajda fse->max_width = S5K5BAF_CIS_WIDTH; 12347d459937SAndrzej Hajda fse->min_height = S5K5BAF_CIS_HEIGHT; 12357d459937SAndrzej Hajda fse->max_height = S5K5BAF_CIS_HEIGHT; 12367d459937SAndrzej Hajda return 0; 12377d459937SAndrzej Hajda } 12387d459937SAndrzej Hajda 12397d459937SAndrzej Hajda i = ARRAY_SIZE(s5k5baf_formats); 12407d459937SAndrzej Hajda while (--i) 12417d459937SAndrzej Hajda if (fse->code == s5k5baf_formats[i].code) 12427d459937SAndrzej Hajda break; 12437d459937SAndrzej Hajda fse->code = s5k5baf_formats[i].code; 12447d459937SAndrzej Hajda fse->min_width = S5K5BAF_WIN_WIDTH_MIN; 12457d459937SAndrzej Hajda fse->max_width = S5K5BAF_CIS_WIDTH; 12467d459937SAndrzej Hajda fse->max_height = S5K5BAF_WIN_HEIGHT_MIN; 12477d459937SAndrzej Hajda fse->min_height = S5K5BAF_CIS_HEIGHT; 12487d459937SAndrzej Hajda 12497d459937SAndrzej Hajda return 0; 12507d459937SAndrzej Hajda } 12517d459937SAndrzej Hajda 12527d459937SAndrzej Hajda static void s5k5baf_try_cis_format(struct v4l2_mbus_framefmt *mf) 12537d459937SAndrzej Hajda { 12547d459937SAndrzej Hajda mf->width = S5K5BAF_CIS_WIDTH; 12557d459937SAndrzej Hajda mf->height = S5K5BAF_CIS_HEIGHT; 1256f5fe58fdSBoris BREZILLON mf->code = MEDIA_BUS_FMT_FIXED; 12577d459937SAndrzej Hajda mf->colorspace = V4L2_COLORSPACE_JPEG; 12587d459937SAndrzej Hajda mf->field = V4L2_FIELD_NONE; 12597d459937SAndrzej Hajda } 12607d459937SAndrzej Hajda 12617d459937SAndrzej Hajda static int s5k5baf_try_isp_format(struct v4l2_mbus_framefmt *mf) 12627d459937SAndrzej Hajda { 12637d459937SAndrzej Hajda int pixfmt; 12647d459937SAndrzej Hajda 12657d459937SAndrzej Hajda v4l_bound_align_image(&mf->width, S5K5BAF_WIN_WIDTH_MIN, 12667d459937SAndrzej Hajda S5K5BAF_CIS_WIDTH, 1, 12677d459937SAndrzej Hajda &mf->height, S5K5BAF_WIN_HEIGHT_MIN, 12687d459937SAndrzej Hajda S5K5BAF_CIS_HEIGHT, 1, 0); 12697d459937SAndrzej Hajda 12707d459937SAndrzej Hajda pixfmt = s5k5baf_find_pixfmt(mf); 12717d459937SAndrzej Hajda 12727d459937SAndrzej Hajda mf->colorspace = s5k5baf_formats[pixfmt].colorspace; 12737d459937SAndrzej Hajda mf->code = s5k5baf_formats[pixfmt].code; 12747d459937SAndrzej Hajda mf->field = V4L2_FIELD_NONE; 12757d459937SAndrzej Hajda 12767d459937SAndrzej Hajda return pixfmt; 12777d459937SAndrzej Hajda } 12787d459937SAndrzej Hajda 1279f7234138SHans Verkuil static int s5k5baf_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, 12807d459937SAndrzej Hajda struct v4l2_subdev_format *fmt) 12817d459937SAndrzej Hajda { 12827d459937SAndrzej Hajda struct s5k5baf *state = to_s5k5baf(sd); 12837d459937SAndrzej Hajda const struct s5k5baf_pixfmt *pixfmt; 12847d459937SAndrzej Hajda struct v4l2_mbus_framefmt *mf; 12857d459937SAndrzej Hajda 12867d459937SAndrzej Hajda if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { 1287f7234138SHans Verkuil mf = v4l2_subdev_get_try_format(sd, cfg, fmt->pad); 12887d459937SAndrzej Hajda fmt->format = *mf; 12897d459937SAndrzej Hajda return 0; 12907d459937SAndrzej Hajda } 12917d459937SAndrzej Hajda 12927d459937SAndrzej Hajda mf = &fmt->format; 12937d459937SAndrzej Hajda if (fmt->pad == PAD_CIS) { 12947d459937SAndrzej Hajda s5k5baf_try_cis_format(mf); 12957d459937SAndrzej Hajda return 0; 12967d459937SAndrzej Hajda } 12977d459937SAndrzej Hajda mf->field = V4L2_FIELD_NONE; 12987d459937SAndrzej Hajda mutex_lock(&state->lock); 12997d459937SAndrzej Hajda pixfmt = &s5k5baf_formats[state->pixfmt]; 13007d459937SAndrzej Hajda mf->width = state->crop_source.width; 13017d459937SAndrzej Hajda mf->height = state->crop_source.height; 13027d459937SAndrzej Hajda mf->code = pixfmt->code; 13037d459937SAndrzej Hajda mf->colorspace = pixfmt->colorspace; 13047d459937SAndrzej Hajda mutex_unlock(&state->lock); 13057d459937SAndrzej Hajda 13067d459937SAndrzej Hajda return 0; 13077d459937SAndrzej Hajda } 13087d459937SAndrzej Hajda 1309f7234138SHans Verkuil static int s5k5baf_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, 13107d459937SAndrzej Hajda struct v4l2_subdev_format *fmt) 13117d459937SAndrzej Hajda { 13127d459937SAndrzej Hajda struct v4l2_mbus_framefmt *mf = &fmt->format; 13137d459937SAndrzej Hajda struct s5k5baf *state = to_s5k5baf(sd); 13147d459937SAndrzej Hajda const struct s5k5baf_pixfmt *pixfmt; 13157d459937SAndrzej Hajda int ret = 0; 13167d459937SAndrzej Hajda 13172f7844ecSLaurent Pinchart mf->field = V4L2_FIELD_NONE; 13182f7844ecSLaurent Pinchart 13197d459937SAndrzej Hajda if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { 1320f7234138SHans Verkuil *v4l2_subdev_get_try_format(sd, cfg, fmt->pad) = *mf; 13217d459937SAndrzej Hajda return 0; 13227d459937SAndrzej Hajda } 13237d459937SAndrzej Hajda 13247d459937SAndrzej Hajda if (fmt->pad == PAD_CIS) { 13257d459937SAndrzej Hajda s5k5baf_try_cis_format(mf); 13267d459937SAndrzej Hajda return 0; 13277d459937SAndrzej Hajda } 13287d459937SAndrzej Hajda 13297d459937SAndrzej Hajda mutex_lock(&state->lock); 13307d459937SAndrzej Hajda 13317d459937SAndrzej Hajda if (state->streaming) { 13327d459937SAndrzej Hajda mutex_unlock(&state->lock); 13337d459937SAndrzej Hajda return -EBUSY; 13347d459937SAndrzej Hajda } 13357d459937SAndrzej Hajda 13367d459937SAndrzej Hajda state->pixfmt = s5k5baf_try_isp_format(mf); 13377d459937SAndrzej Hajda pixfmt = &s5k5baf_formats[state->pixfmt]; 13387d459937SAndrzej Hajda mf->code = pixfmt->code; 13397d459937SAndrzej Hajda mf->colorspace = pixfmt->colorspace; 13407d459937SAndrzej Hajda mf->width = state->crop_source.width; 13417d459937SAndrzej Hajda mf->height = state->crop_source.height; 13427d459937SAndrzej Hajda 13437d459937SAndrzej Hajda mutex_unlock(&state->lock); 13447d459937SAndrzej Hajda return ret; 13457d459937SAndrzej Hajda } 13467d459937SAndrzej Hajda 13477d459937SAndrzej Hajda enum selection_rect { R_CIS, R_CROP_SINK, R_COMPOSE, R_CROP_SOURCE, R_INVALID }; 13487d459937SAndrzej Hajda 13497d459937SAndrzej Hajda static enum selection_rect s5k5baf_get_sel_rect(u32 pad, u32 target) 13507d459937SAndrzej Hajda { 13517d459937SAndrzej Hajda switch (target) { 13527d459937SAndrzej Hajda case V4L2_SEL_TGT_CROP_BOUNDS: 13537d459937SAndrzej Hajda return pad ? R_COMPOSE : R_CIS; 13547d459937SAndrzej Hajda case V4L2_SEL_TGT_CROP: 13557d459937SAndrzej Hajda return pad ? R_CROP_SOURCE : R_CROP_SINK; 13567d459937SAndrzej Hajda case V4L2_SEL_TGT_COMPOSE_BOUNDS: 13577d459937SAndrzej Hajda return pad ? R_INVALID : R_CROP_SINK; 13587d459937SAndrzej Hajda case V4L2_SEL_TGT_COMPOSE: 13597d459937SAndrzej Hajda return pad ? R_INVALID : R_COMPOSE; 13607d459937SAndrzej Hajda default: 13617d459937SAndrzej Hajda return R_INVALID; 13627d459937SAndrzej Hajda } 13637d459937SAndrzej Hajda } 13647d459937SAndrzej Hajda 13657d459937SAndrzej Hajda static int s5k5baf_is_bound_target(u32 target) 13667d459937SAndrzej Hajda { 13677296e158SSachin Kamat return target == V4L2_SEL_TGT_CROP_BOUNDS || 13687296e158SSachin Kamat target == V4L2_SEL_TGT_COMPOSE_BOUNDS; 13697d459937SAndrzej Hajda } 13707d459937SAndrzej Hajda 13717d459937SAndrzej Hajda static int s5k5baf_get_selection(struct v4l2_subdev *sd, 1372f7234138SHans Verkuil struct v4l2_subdev_pad_config *cfg, 13737d459937SAndrzej Hajda struct v4l2_subdev_selection *sel) 13747d459937SAndrzej Hajda { 13757d459937SAndrzej Hajda static enum selection_rect rtype; 13767d459937SAndrzej Hajda struct s5k5baf *state = to_s5k5baf(sd); 13777d459937SAndrzej Hajda 13787d459937SAndrzej Hajda rtype = s5k5baf_get_sel_rect(sel->pad, sel->target); 13797d459937SAndrzej Hajda 13807d459937SAndrzej Hajda switch (rtype) { 13817d459937SAndrzej Hajda case R_INVALID: 13827d459937SAndrzej Hajda return -EINVAL; 13837d459937SAndrzej Hajda case R_CIS: 13847d459937SAndrzej Hajda sel->r = s5k5baf_cis_rect; 13857d459937SAndrzej Hajda return 0; 13867d459937SAndrzej Hajda default: 13877d459937SAndrzej Hajda break; 13887d459937SAndrzej Hajda } 13897d459937SAndrzej Hajda 13907d459937SAndrzej Hajda if (sel->which == V4L2_SUBDEV_FORMAT_TRY) { 13917d459937SAndrzej Hajda if (rtype == R_COMPOSE) 1392f7234138SHans Verkuil sel->r = *v4l2_subdev_get_try_compose(sd, cfg, sel->pad); 13937d459937SAndrzej Hajda else 1394f7234138SHans Verkuil sel->r = *v4l2_subdev_get_try_crop(sd, cfg, sel->pad); 13957d459937SAndrzej Hajda return 0; 13967d459937SAndrzej Hajda } 13977d459937SAndrzej Hajda 13987d459937SAndrzej Hajda mutex_lock(&state->lock); 13997d459937SAndrzej Hajda switch (rtype) { 14007d459937SAndrzej Hajda case R_CROP_SINK: 14017d459937SAndrzej Hajda sel->r = state->crop_sink; 14027d459937SAndrzej Hajda break; 14037d459937SAndrzej Hajda case R_COMPOSE: 14047d459937SAndrzej Hajda sel->r = state->compose; 14057d459937SAndrzej Hajda break; 14067d459937SAndrzej Hajda case R_CROP_SOURCE: 14077d459937SAndrzej Hajda sel->r = state->crop_source; 14087d459937SAndrzej Hajda break; 14097d459937SAndrzej Hajda default: 14107d459937SAndrzej Hajda break; 14117d459937SAndrzej Hajda } 14127d459937SAndrzej Hajda if (s5k5baf_is_bound_target(sel->target)) { 14137d459937SAndrzej Hajda sel->r.left = 0; 14147d459937SAndrzej Hajda sel->r.top = 0; 14157d459937SAndrzej Hajda } 14167d459937SAndrzej Hajda mutex_unlock(&state->lock); 14177d459937SAndrzej Hajda 14187d459937SAndrzej Hajda return 0; 14197d459937SAndrzej Hajda } 14207d459937SAndrzej Hajda 14217d459937SAndrzej Hajda /* bounds range [start, start+len) to [0, max) and aligns to 2 */ 14227d459937SAndrzej Hajda static void s5k5baf_bound_range(u32 *start, u32 *len, u32 max) 14237d459937SAndrzej Hajda { 14247d459937SAndrzej Hajda if (*len > max) 14257d459937SAndrzej Hajda *len = max; 14267d459937SAndrzej Hajda if (*start + *len > max) 14277d459937SAndrzej Hajda *start = max - *len; 14287d459937SAndrzej Hajda *start &= ~1; 14297d459937SAndrzej Hajda *len &= ~1; 14307d459937SAndrzej Hajda if (*len < S5K5BAF_WIN_WIDTH_MIN) 14317d459937SAndrzej Hajda *len = S5K5BAF_WIN_WIDTH_MIN; 14327d459937SAndrzej Hajda } 14337d459937SAndrzej Hajda 14347d459937SAndrzej Hajda static void s5k5baf_bound_rect(struct v4l2_rect *r, u32 width, u32 height) 14357d459937SAndrzej Hajda { 14367d459937SAndrzej Hajda s5k5baf_bound_range(&r->left, &r->width, width); 14377d459937SAndrzej Hajda s5k5baf_bound_range(&r->top, &r->height, height); 14387d459937SAndrzej Hajda } 14397d459937SAndrzej Hajda 14407d459937SAndrzej Hajda static void s5k5baf_set_rect_and_adjust(struct v4l2_rect **rects, 14417d459937SAndrzej Hajda enum selection_rect first, 14427d459937SAndrzej Hajda struct v4l2_rect *v) 14437d459937SAndrzej Hajda { 14447d459937SAndrzej Hajda struct v4l2_rect *r, *br; 14457d459937SAndrzej Hajda enum selection_rect i = first; 14467d459937SAndrzej Hajda 14477d459937SAndrzej Hajda *rects[first] = *v; 14487d459937SAndrzej Hajda do { 14497d459937SAndrzej Hajda r = rects[i]; 14507d459937SAndrzej Hajda br = rects[i - 1]; 14517d459937SAndrzej Hajda s5k5baf_bound_rect(r, br->width, br->height); 14527d459937SAndrzej Hajda } while (++i != R_INVALID); 14537d459937SAndrzej Hajda *v = *rects[first]; 14547d459937SAndrzej Hajda } 14557d459937SAndrzej Hajda 14567d459937SAndrzej Hajda static bool s5k5baf_cmp_rect(const struct v4l2_rect *r1, 14577d459937SAndrzej Hajda const struct v4l2_rect *r2) 14587d459937SAndrzej Hajda { 14597d459937SAndrzej Hajda return !memcmp(r1, r2, sizeof(*r1)); 14607d459937SAndrzej Hajda } 14617d459937SAndrzej Hajda 14627d459937SAndrzej Hajda static int s5k5baf_set_selection(struct v4l2_subdev *sd, 1463f7234138SHans Verkuil struct v4l2_subdev_pad_config *cfg, 14647d459937SAndrzej Hajda struct v4l2_subdev_selection *sel) 14657d459937SAndrzej Hajda { 14667d459937SAndrzej Hajda static enum selection_rect rtype; 14677d459937SAndrzej Hajda struct s5k5baf *state = to_s5k5baf(sd); 14687d459937SAndrzej Hajda struct v4l2_rect **rects; 14697d459937SAndrzej Hajda int ret = 0; 14707d459937SAndrzej Hajda 14717d459937SAndrzej Hajda rtype = s5k5baf_get_sel_rect(sel->pad, sel->target); 14727d459937SAndrzej Hajda if (rtype == R_INVALID || s5k5baf_is_bound_target(sel->target)) 14737d459937SAndrzej Hajda return -EINVAL; 14747d459937SAndrzej Hajda 14757d459937SAndrzej Hajda /* allow only scaling on compose */ 14767d459937SAndrzej Hajda if (rtype == R_COMPOSE) { 14777d459937SAndrzej Hajda sel->r.left = 0; 14787d459937SAndrzej Hajda sel->r.top = 0; 14797d459937SAndrzej Hajda } 14807d459937SAndrzej Hajda 14817d459937SAndrzej Hajda if (sel->which == V4L2_SUBDEV_FORMAT_TRY) { 14827d459937SAndrzej Hajda rects = (struct v4l2_rect * []) { 14837d459937SAndrzej Hajda &s5k5baf_cis_rect, 1484f7234138SHans Verkuil v4l2_subdev_get_try_crop(sd, cfg, PAD_CIS), 1485f7234138SHans Verkuil v4l2_subdev_get_try_compose(sd, cfg, PAD_CIS), 1486f7234138SHans Verkuil v4l2_subdev_get_try_crop(sd, cfg, PAD_OUT) 14877d459937SAndrzej Hajda }; 14887d459937SAndrzej Hajda s5k5baf_set_rect_and_adjust(rects, rtype, &sel->r); 14897d459937SAndrzej Hajda return 0; 14907d459937SAndrzej Hajda } 14917d459937SAndrzej Hajda 14927d459937SAndrzej Hajda rects = (struct v4l2_rect * []) { 14937d459937SAndrzej Hajda &s5k5baf_cis_rect, 14947d459937SAndrzej Hajda &state->crop_sink, 14957d459937SAndrzej Hajda &state->compose, 14967d459937SAndrzej Hajda &state->crop_source 14977d459937SAndrzej Hajda }; 14987d459937SAndrzej Hajda mutex_lock(&state->lock); 14997d459937SAndrzej Hajda if (state->streaming) { 15007d459937SAndrzej Hajda /* adjust sel->r to avoid output resolution change */ 15017d459937SAndrzej Hajda if (rtype < R_CROP_SOURCE) { 15027d459937SAndrzej Hajda if (sel->r.width < state->crop_source.width) 15037d459937SAndrzej Hajda sel->r.width = state->crop_source.width; 15047d459937SAndrzej Hajda if (sel->r.height < state->crop_source.height) 15057d459937SAndrzej Hajda sel->r.height = state->crop_source.height; 15067d459937SAndrzej Hajda } else { 15077d459937SAndrzej Hajda sel->r.width = state->crop_source.width; 15087d459937SAndrzej Hajda sel->r.height = state->crop_source.height; 15097d459937SAndrzej Hajda } 15107d459937SAndrzej Hajda } 15117d459937SAndrzej Hajda s5k5baf_set_rect_and_adjust(rects, rtype, &sel->r); 15127d459937SAndrzej Hajda if (!s5k5baf_cmp_rect(&state->crop_sink, &s5k5baf_cis_rect) || 15137d459937SAndrzej Hajda !s5k5baf_cmp_rect(&state->compose, &s5k5baf_cis_rect)) 15147d459937SAndrzej Hajda state->apply_crop = 1; 15157d459937SAndrzej Hajda if (state->streaming) 15167d459937SAndrzej Hajda ret = s5k5baf_hw_set_crop_rects(state); 15177d459937SAndrzej Hajda mutex_unlock(&state->lock); 15187d459937SAndrzej Hajda 15197d459937SAndrzej Hajda return ret; 15207d459937SAndrzej Hajda } 15217d459937SAndrzej Hajda 15227d459937SAndrzej Hajda static const struct v4l2_subdev_pad_ops s5k5baf_cis_pad_ops = { 15237d459937SAndrzej Hajda .enum_mbus_code = s5k5baf_enum_mbus_code, 15247d459937SAndrzej Hajda .enum_frame_size = s5k5baf_enum_frame_size, 15257d459937SAndrzej Hajda .get_fmt = s5k5baf_get_fmt, 15267d459937SAndrzej Hajda .set_fmt = s5k5baf_set_fmt, 15277d459937SAndrzej Hajda }; 15287d459937SAndrzej Hajda 15297d459937SAndrzej Hajda static const struct v4l2_subdev_pad_ops s5k5baf_pad_ops = { 15307d459937SAndrzej Hajda .enum_mbus_code = s5k5baf_enum_mbus_code, 15317d459937SAndrzej Hajda .enum_frame_size = s5k5baf_enum_frame_size, 15327d459937SAndrzej Hajda .enum_frame_interval = s5k5baf_enum_frame_interval, 15337d459937SAndrzej Hajda .get_fmt = s5k5baf_get_fmt, 15347d459937SAndrzej Hajda .set_fmt = s5k5baf_set_fmt, 15357d459937SAndrzej Hajda .get_selection = s5k5baf_get_selection, 15367d459937SAndrzej Hajda .set_selection = s5k5baf_set_selection, 15377d459937SAndrzej Hajda }; 15387d459937SAndrzej Hajda 15397d459937SAndrzej Hajda static const struct v4l2_subdev_video_ops s5k5baf_video_ops = { 15407d459937SAndrzej Hajda .g_frame_interval = s5k5baf_g_frame_interval, 15417d459937SAndrzej Hajda .s_frame_interval = s5k5baf_s_frame_interval, 15427d459937SAndrzej Hajda .s_stream = s5k5baf_s_stream, 15437d459937SAndrzej Hajda }; 15447d459937SAndrzej Hajda 15457d459937SAndrzej Hajda /* 15467d459937SAndrzej Hajda * V4L2 subdev controls 15477d459937SAndrzej Hajda */ 15487d459937SAndrzej Hajda 15497d459937SAndrzej Hajda static int s5k5baf_s_ctrl(struct v4l2_ctrl *ctrl) 15507d459937SAndrzej Hajda { 15517d459937SAndrzej Hajda struct v4l2_subdev *sd = ctrl_to_sd(ctrl); 15527d459937SAndrzej Hajda struct s5k5baf *state = to_s5k5baf(sd); 15537d459937SAndrzej Hajda int ret; 15547d459937SAndrzej Hajda 15557d459937SAndrzej Hajda v4l2_dbg(1, debug, sd, "ctrl: %s, value: %d\n", ctrl->name, ctrl->val); 15567d459937SAndrzej Hajda 15577d459937SAndrzej Hajda mutex_lock(&state->lock); 15587d459937SAndrzej Hajda 15597d459937SAndrzej Hajda if (state->power == 0) 15607d459937SAndrzej Hajda goto unlock; 15617d459937SAndrzej Hajda 15627d459937SAndrzej Hajda switch (ctrl->id) { 15637d459937SAndrzej Hajda case V4L2_CID_AUTO_WHITE_BALANCE: 15647d459937SAndrzej Hajda s5k5baf_hw_set_awb(state, ctrl->val); 15657d459937SAndrzej Hajda break; 15667d459937SAndrzej Hajda 15677d459937SAndrzej Hajda case V4L2_CID_BRIGHTNESS: 15687d459937SAndrzej Hajda s5k5baf_write(state, REG_USER_BRIGHTNESS, ctrl->val); 15697d459937SAndrzej Hajda break; 15707d459937SAndrzej Hajda 15717d459937SAndrzej Hajda case V4L2_CID_COLORFX: 15727d459937SAndrzej Hajda s5k5baf_hw_set_colorfx(state, ctrl->val); 15737d459937SAndrzej Hajda break; 15747d459937SAndrzej Hajda 15757d459937SAndrzej Hajda case V4L2_CID_CONTRAST: 15767d459937SAndrzej Hajda s5k5baf_write(state, REG_USER_CONTRAST, ctrl->val); 15777d459937SAndrzej Hajda break; 15787d459937SAndrzej Hajda 15797d459937SAndrzej Hajda case V4L2_CID_EXPOSURE_AUTO: 15807d459937SAndrzej Hajda s5k5baf_hw_set_auto_exposure(state, ctrl->val); 15817d459937SAndrzej Hajda break; 15827d459937SAndrzej Hajda 15837d459937SAndrzej Hajda case V4L2_CID_HFLIP: 15847d459937SAndrzej Hajda s5k5baf_hw_set_mirror(state); 15857d459937SAndrzej Hajda break; 15867d459937SAndrzej Hajda 15877d459937SAndrzej Hajda case V4L2_CID_POWER_LINE_FREQUENCY: 15887d459937SAndrzej Hajda s5k5baf_hw_set_anti_flicker(state, ctrl->val); 15897d459937SAndrzej Hajda break; 15907d459937SAndrzej Hajda 15917d459937SAndrzej Hajda case V4L2_CID_SATURATION: 15927d459937SAndrzej Hajda s5k5baf_write(state, REG_USER_SATURATION, ctrl->val); 15937d459937SAndrzej Hajda break; 15947d459937SAndrzej Hajda 15957d459937SAndrzej Hajda case V4L2_CID_SHARPNESS: 15967d459937SAndrzej Hajda s5k5baf_write(state, REG_USER_SHARPBLUR, ctrl->val); 15977d459937SAndrzej Hajda break; 15987d459937SAndrzej Hajda 15997d459937SAndrzej Hajda case V4L2_CID_WHITE_BALANCE_TEMPERATURE: 16007d459937SAndrzej Hajda s5k5baf_write(state, REG_P_COLORTEMP(0), ctrl->val); 16017d459937SAndrzej Hajda if (state->apply_cfg) 16027d459937SAndrzej Hajda s5k5baf_hw_sync_cfg(state); 16037d459937SAndrzej Hajda break; 16047d459937SAndrzej Hajda 16057d459937SAndrzej Hajda case V4L2_CID_TEST_PATTERN: 16067d459937SAndrzej Hajda s5k5baf_hw_set_test_pattern(state, ctrl->val); 16077d459937SAndrzej Hajda break; 16087d459937SAndrzej Hajda } 16097d459937SAndrzej Hajda unlock: 16107d459937SAndrzej Hajda ret = s5k5baf_clear_error(state); 16117d459937SAndrzej Hajda mutex_unlock(&state->lock); 16127d459937SAndrzej Hajda return ret; 16137d459937SAndrzej Hajda } 16147d459937SAndrzej Hajda 16157d459937SAndrzej Hajda static const struct v4l2_ctrl_ops s5k5baf_ctrl_ops = { 16167d459937SAndrzej Hajda .s_ctrl = s5k5baf_s_ctrl, 16177d459937SAndrzej Hajda }; 16187d459937SAndrzej Hajda 16197d459937SAndrzej Hajda static const char * const s5k5baf_test_pattern_menu[] = { 16207d459937SAndrzej Hajda "Disabled", 16217d459937SAndrzej Hajda "Blank", 16227d459937SAndrzej Hajda "Bars", 16237d459937SAndrzej Hajda "Gradients", 16247d459937SAndrzej Hajda "Textile", 16257d459937SAndrzej Hajda "Textile2", 16267d459937SAndrzej Hajda "Squares" 16277d459937SAndrzej Hajda }; 16287d459937SAndrzej Hajda 16297d459937SAndrzej Hajda static int s5k5baf_initialize_ctrls(struct s5k5baf *state) 16307d459937SAndrzej Hajda { 16317d459937SAndrzej Hajda const struct v4l2_ctrl_ops *ops = &s5k5baf_ctrl_ops; 16327d459937SAndrzej Hajda struct s5k5baf_ctrls *ctrls = &state->ctrls; 16337d459937SAndrzej Hajda struct v4l2_ctrl_handler *hdl = &ctrls->handler; 16347d459937SAndrzej Hajda int ret; 16357d459937SAndrzej Hajda 16367d459937SAndrzej Hajda ret = v4l2_ctrl_handler_init(hdl, 16); 16377d459937SAndrzej Hajda if (ret < 0) { 16387d459937SAndrzej Hajda v4l2_err(&state->sd, "cannot init ctrl handler (%d)\n", ret); 16397d459937SAndrzej Hajda return ret; 16407d459937SAndrzej Hajda } 16417d459937SAndrzej Hajda 16427d459937SAndrzej Hajda /* Auto white balance cluster */ 16437d459937SAndrzej Hajda ctrls->awb = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_AUTO_WHITE_BALANCE, 16447d459937SAndrzej Hajda 0, 1, 1, 1); 16457d459937SAndrzej Hajda ctrls->gain_red = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_RED_BALANCE, 16467d459937SAndrzej Hajda 0, 255, 1, S5K5BAF_GAIN_RED_DEF); 16477d459937SAndrzej Hajda ctrls->gain_blue = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_BLUE_BALANCE, 16487d459937SAndrzej Hajda 0, 255, 1, S5K5BAF_GAIN_BLUE_DEF); 16497d459937SAndrzej Hajda v4l2_ctrl_auto_cluster(3, &ctrls->awb, 0, false); 16507d459937SAndrzej Hajda 16517d459937SAndrzej Hajda ctrls->hflip = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_HFLIP, 0, 1, 1, 0); 16527d459937SAndrzej Hajda ctrls->vflip = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_VFLIP, 0, 1, 1, 0); 16537d459937SAndrzej Hajda v4l2_ctrl_cluster(2, &ctrls->hflip); 16547d459937SAndrzej Hajda 16557d459937SAndrzej Hajda ctrls->auto_exp = v4l2_ctrl_new_std_menu(hdl, ops, 16567d459937SAndrzej Hajda V4L2_CID_EXPOSURE_AUTO, 16577d459937SAndrzej Hajda V4L2_EXPOSURE_MANUAL, 0, V4L2_EXPOSURE_AUTO); 16587d459937SAndrzej Hajda /* Exposure time: x 1 us */ 16597d459937SAndrzej Hajda ctrls->exposure = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_EXPOSURE, 16607d459937SAndrzej Hajda 0, 6000000U, 1, 100000U); 16617d459937SAndrzej Hajda /* Total gain: 256 <=> 1x */ 16627d459937SAndrzej Hajda ctrls->gain = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_GAIN, 16637d459937SAndrzej Hajda 0, 256, 1, 256); 16647d459937SAndrzej Hajda v4l2_ctrl_auto_cluster(3, &ctrls->auto_exp, 0, false); 16657d459937SAndrzej Hajda 16667d459937SAndrzej Hajda v4l2_ctrl_new_std_menu(hdl, ops, V4L2_CID_POWER_LINE_FREQUENCY, 16677d459937SAndrzej Hajda V4L2_CID_POWER_LINE_FREQUENCY_AUTO, 0, 16687d459937SAndrzej Hajda V4L2_CID_POWER_LINE_FREQUENCY_AUTO); 16697d459937SAndrzej Hajda 16707d459937SAndrzej Hajda v4l2_ctrl_new_std_menu(hdl, ops, V4L2_CID_COLORFX, 16717d459937SAndrzej Hajda V4L2_COLORFX_SKY_BLUE, ~0x6f, V4L2_COLORFX_NONE); 16727d459937SAndrzej Hajda 16737d459937SAndrzej Hajda v4l2_ctrl_new_std(hdl, ops, V4L2_CID_WHITE_BALANCE_TEMPERATURE, 16747d459937SAndrzej Hajda 0, 256, 1, 0); 16757d459937SAndrzej Hajda 16767d459937SAndrzej Hajda v4l2_ctrl_new_std(hdl, ops, V4L2_CID_SATURATION, -127, 127, 1, 0); 16777d459937SAndrzej Hajda v4l2_ctrl_new_std(hdl, ops, V4L2_CID_BRIGHTNESS, -127, 127, 1, 0); 16787d459937SAndrzej Hajda v4l2_ctrl_new_std(hdl, ops, V4L2_CID_CONTRAST, -127, 127, 1, 0); 16797d459937SAndrzej Hajda v4l2_ctrl_new_std(hdl, ops, V4L2_CID_SHARPNESS, -127, 127, 1, 0); 16807d459937SAndrzej Hajda 16817d459937SAndrzej Hajda v4l2_ctrl_new_std_menu_items(hdl, ops, V4L2_CID_TEST_PATTERN, 16827d459937SAndrzej Hajda ARRAY_SIZE(s5k5baf_test_pattern_menu) - 1, 16837d459937SAndrzej Hajda 0, 0, s5k5baf_test_pattern_menu); 16847d459937SAndrzej Hajda 16857d459937SAndrzej Hajda if (hdl->error) { 16867d459937SAndrzej Hajda v4l2_err(&state->sd, "error creating controls (%d)\n", 16877d459937SAndrzej Hajda hdl->error); 16887d459937SAndrzej Hajda ret = hdl->error; 16897d459937SAndrzej Hajda v4l2_ctrl_handler_free(hdl); 16907d459937SAndrzej Hajda return ret; 16917d459937SAndrzej Hajda } 16927d459937SAndrzej Hajda 16937d459937SAndrzej Hajda state->sd.ctrl_handler = hdl; 16947d459937SAndrzej Hajda return 0; 16957d459937SAndrzej Hajda } 16967d459937SAndrzej Hajda 16977d459937SAndrzej Hajda /* 16987d459937SAndrzej Hajda * V4L2 subdev internal operations 16997d459937SAndrzej Hajda */ 17007d459937SAndrzej Hajda static int s5k5baf_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) 17017d459937SAndrzej Hajda { 17027d459937SAndrzej Hajda struct v4l2_mbus_framefmt *mf; 17037d459937SAndrzej Hajda 1704f7234138SHans Verkuil mf = v4l2_subdev_get_try_format(sd, fh->pad, PAD_CIS); 17057d459937SAndrzej Hajda s5k5baf_try_cis_format(mf); 17067d459937SAndrzej Hajda 17077d459937SAndrzej Hajda if (s5k5baf_is_cis_subdev(sd)) 17087d459937SAndrzej Hajda return 0; 17097d459937SAndrzej Hajda 1710f7234138SHans Verkuil mf = v4l2_subdev_get_try_format(sd, fh->pad, PAD_OUT); 17117d459937SAndrzej Hajda mf->colorspace = s5k5baf_formats[0].colorspace; 17127d459937SAndrzej Hajda mf->code = s5k5baf_formats[0].code; 17137d459937SAndrzej Hajda mf->width = s5k5baf_cis_rect.width; 17147d459937SAndrzej Hajda mf->height = s5k5baf_cis_rect.height; 17157d459937SAndrzej Hajda mf->field = V4L2_FIELD_NONE; 17167d459937SAndrzej Hajda 1717f7234138SHans Verkuil *v4l2_subdev_get_try_crop(sd, fh->pad, PAD_CIS) = s5k5baf_cis_rect; 1718f7234138SHans Verkuil *v4l2_subdev_get_try_compose(sd, fh->pad, PAD_CIS) = s5k5baf_cis_rect; 1719f7234138SHans Verkuil *v4l2_subdev_get_try_crop(sd, fh->pad, PAD_OUT) = s5k5baf_cis_rect; 17207d459937SAndrzej Hajda 17217d459937SAndrzej Hajda return 0; 17227d459937SAndrzej Hajda } 17237d459937SAndrzej Hajda 17247d459937SAndrzej Hajda static int s5k5baf_check_fw_revision(struct s5k5baf *state) 17257d459937SAndrzej Hajda { 17267d459937SAndrzej Hajda u16 api_ver = 0, fw_rev = 0, s_id = 0; 17277d459937SAndrzej Hajda int ret; 17287d459937SAndrzej Hajda 17297d459937SAndrzej Hajda api_ver = s5k5baf_read(state, REG_FW_APIVER); 17307d459937SAndrzej Hajda fw_rev = s5k5baf_read(state, REG_FW_REVISION) & 0xff; 17317d459937SAndrzej Hajda s_id = s5k5baf_read(state, REG_FW_SENSOR_ID); 17327d459937SAndrzej Hajda ret = s5k5baf_clear_error(state); 17337d459937SAndrzej Hajda if (ret < 0) 17347d459937SAndrzej Hajda return ret; 17357d459937SAndrzej Hajda 17367d459937SAndrzej Hajda v4l2_info(&state->sd, "FW API=%#x, revision=%#x sensor_id=%#x\n", 17377d459937SAndrzej Hajda api_ver, fw_rev, s_id); 17387d459937SAndrzej Hajda 17397d459937SAndrzej Hajda if (api_ver != S5K5BAF_FW_APIVER) { 17407d459937SAndrzej Hajda v4l2_err(&state->sd, "FW API version not supported\n"); 17417d459937SAndrzej Hajda return -ENODEV; 17427d459937SAndrzej Hajda } 17437d459937SAndrzej Hajda 17447d459937SAndrzej Hajda return 0; 17457d459937SAndrzej Hajda } 17467d459937SAndrzej Hajda 17477d459937SAndrzej Hajda static int s5k5baf_registered(struct v4l2_subdev *sd) 17487d459937SAndrzej Hajda { 17497d459937SAndrzej Hajda struct s5k5baf *state = to_s5k5baf(sd); 17507d459937SAndrzej Hajda int ret; 17517d459937SAndrzej Hajda 17527d459937SAndrzej Hajda ret = v4l2_device_register_subdev(sd->v4l2_dev, &state->cis_sd); 17537d459937SAndrzej Hajda if (ret < 0) 17547d459937SAndrzej Hajda v4l2_err(sd, "failed to register subdev %s\n", 17557d459937SAndrzej Hajda state->cis_sd.name); 17567d459937SAndrzej Hajda else 17577d459937SAndrzej Hajda ret = media_entity_create_link(&state->cis_sd.entity, PAD_CIS, 17587d459937SAndrzej Hajda &state->sd.entity, PAD_CIS, 17597d459937SAndrzej Hajda MEDIA_LNK_FL_IMMUTABLE | 17607d459937SAndrzej Hajda MEDIA_LNK_FL_ENABLED); 17617d459937SAndrzej Hajda return ret; 17627d459937SAndrzej Hajda } 17637d459937SAndrzej Hajda 17647d459937SAndrzej Hajda static void s5k5baf_unregistered(struct v4l2_subdev *sd) 17657d459937SAndrzej Hajda { 17667d459937SAndrzej Hajda struct s5k5baf *state = to_s5k5baf(sd); 17677d459937SAndrzej Hajda v4l2_device_unregister_subdev(&state->cis_sd); 17687d459937SAndrzej Hajda } 17697d459937SAndrzej Hajda 17707d459937SAndrzej Hajda static const struct v4l2_subdev_ops s5k5baf_cis_subdev_ops = { 17717d459937SAndrzej Hajda .pad = &s5k5baf_cis_pad_ops, 17727d459937SAndrzej Hajda }; 17737d459937SAndrzej Hajda 17747d459937SAndrzej Hajda static const struct v4l2_subdev_internal_ops s5k5baf_cis_subdev_internal_ops = { 17757d459937SAndrzej Hajda .open = s5k5baf_open, 17767d459937SAndrzej Hajda }; 17777d459937SAndrzej Hajda 17787d459937SAndrzej Hajda static const struct v4l2_subdev_internal_ops s5k5baf_subdev_internal_ops = { 17797d459937SAndrzej Hajda .registered = s5k5baf_registered, 17807d459937SAndrzej Hajda .unregistered = s5k5baf_unregistered, 17817d459937SAndrzej Hajda .open = s5k5baf_open, 17827d459937SAndrzej Hajda }; 17837d459937SAndrzej Hajda 17847d459937SAndrzej Hajda static const struct v4l2_subdev_core_ops s5k5baf_core_ops = { 17857d459937SAndrzej Hajda .s_power = s5k5baf_set_power, 17867d459937SAndrzej Hajda .log_status = v4l2_ctrl_subdev_log_status, 17877d459937SAndrzej Hajda }; 17887d459937SAndrzej Hajda 17897d459937SAndrzej Hajda static const struct v4l2_subdev_ops s5k5baf_subdev_ops = { 17907d459937SAndrzej Hajda .core = &s5k5baf_core_ops, 17917d459937SAndrzej Hajda .pad = &s5k5baf_pad_ops, 17927d459937SAndrzej Hajda .video = &s5k5baf_video_ops, 17937d459937SAndrzej Hajda }; 17947d459937SAndrzej Hajda 17957d459937SAndrzej Hajda static int s5k5baf_configure_gpios(struct s5k5baf *state) 17967d459937SAndrzej Hajda { 17970348bb1aSHans Verkuil static const char * const name[] = { "S5K5BAF_STBY", "S5K5BAF_RST" }; 17987d459937SAndrzej Hajda struct i2c_client *c = v4l2_get_subdevdata(&state->sd); 17997d459937SAndrzej Hajda struct s5k5baf_gpio *g = state->gpios; 18007d459937SAndrzej Hajda int ret, i; 18017d459937SAndrzej Hajda 18027d459937SAndrzej Hajda for (i = 0; i < NUM_GPIOS; ++i) { 18037d459937SAndrzej Hajda int flags = GPIOF_DIR_OUT; 18047d459937SAndrzej Hajda if (g[i].level) 18057d459937SAndrzej Hajda flags |= GPIOF_INIT_HIGH; 18067d459937SAndrzej Hajda ret = devm_gpio_request_one(&c->dev, g[i].gpio, flags, name[i]); 18077d459937SAndrzej Hajda if (ret < 0) { 18087d459937SAndrzej Hajda v4l2_err(c, "failed to request gpio %s\n", name[i]); 18097d459937SAndrzej Hajda return ret; 18107d459937SAndrzej Hajda } 18117d459937SAndrzej Hajda } 18127d459937SAndrzej Hajda return 0; 18137d459937SAndrzej Hajda } 18147d459937SAndrzej Hajda 18157d459937SAndrzej Hajda static int s5k5baf_parse_gpios(struct s5k5baf_gpio *gpios, struct device *dev) 18167d459937SAndrzej Hajda { 18177d459937SAndrzej Hajda static const char * const names[] = { 18187d459937SAndrzej Hajda "stbyn-gpios", 18197d459937SAndrzej Hajda "rstn-gpios", 18207d459937SAndrzej Hajda }; 18217d459937SAndrzej Hajda struct device_node *node = dev->of_node; 18227d459937SAndrzej Hajda enum of_gpio_flags flags; 18237d459937SAndrzej Hajda int ret, i; 18247d459937SAndrzej Hajda 18257d459937SAndrzej Hajda for (i = 0; i < NUM_GPIOS; ++i) { 18267d459937SAndrzej Hajda ret = of_get_named_gpio_flags(node, names[i], 0, &flags); 18277d459937SAndrzej Hajda if (ret < 0) { 18287d459937SAndrzej Hajda dev_err(dev, "no %s GPIO pin provided\n", names[i]); 18297d459937SAndrzej Hajda return ret; 18307d459937SAndrzej Hajda } 18317d459937SAndrzej Hajda gpios[i].gpio = ret; 18327d459937SAndrzej Hajda gpios[i].level = !(flags & OF_GPIO_ACTIVE_LOW); 18337d459937SAndrzej Hajda } 18347d459937SAndrzej Hajda 18357d459937SAndrzej Hajda return 0; 18367d459937SAndrzej Hajda } 18377d459937SAndrzej Hajda 18387d459937SAndrzej Hajda static int s5k5baf_parse_device_node(struct s5k5baf *state, struct device *dev) 18397d459937SAndrzej Hajda { 18407d459937SAndrzej Hajda struct device_node *node = dev->of_node; 18417d459937SAndrzej Hajda struct device_node *node_ep; 18427d459937SAndrzej Hajda struct v4l2_of_endpoint ep; 18437d459937SAndrzej Hajda int ret; 18447d459937SAndrzej Hajda 18457d459937SAndrzej Hajda if (!node) { 18467d459937SAndrzej Hajda dev_err(dev, "no device-tree node provided\n"); 18477d459937SAndrzej Hajda return -EINVAL; 18487d459937SAndrzej Hajda } 18497d459937SAndrzej Hajda 18507d459937SAndrzej Hajda ret = of_property_read_u32(node, "clock-frequency", 18517d459937SAndrzej Hajda &state->mclk_frequency); 18527d459937SAndrzej Hajda if (ret < 0) { 18537d459937SAndrzej Hajda state->mclk_frequency = S5K5BAF_DEFAULT_MCLK_FREQ; 18547d459937SAndrzej Hajda dev_info(dev, "using default %u Hz clock frequency\n", 18557d459937SAndrzej Hajda state->mclk_frequency); 18567d459937SAndrzej Hajda } 18577d459937SAndrzej Hajda 18587d459937SAndrzej Hajda ret = s5k5baf_parse_gpios(state->gpios, dev); 18597d459937SAndrzej Hajda if (ret < 0) 18607d459937SAndrzej Hajda return ret; 18617d459937SAndrzej Hajda 1862fd9fdb78SPhilipp Zabel node_ep = of_graph_get_next_endpoint(node, NULL); 18637d459937SAndrzej Hajda if (!node_ep) { 18647d459937SAndrzej Hajda dev_err(dev, "no endpoint defined at node %s\n", 18657d459937SAndrzej Hajda node->full_name); 18667d459937SAndrzej Hajda return -EINVAL; 18677d459937SAndrzej Hajda } 18687d459937SAndrzej Hajda 18697d459937SAndrzej Hajda v4l2_of_parse_endpoint(node_ep, &ep); 18707d459937SAndrzej Hajda of_node_put(node_ep); 18717d459937SAndrzej Hajda state->bus_type = ep.bus_type; 18727d459937SAndrzej Hajda 18737d459937SAndrzej Hajda switch (state->bus_type) { 18747d459937SAndrzej Hajda case V4L2_MBUS_CSI2: 18757d459937SAndrzej Hajda state->nlanes = ep.bus.mipi_csi2.num_data_lanes; 18767d459937SAndrzej Hajda break; 18777d459937SAndrzej Hajda case V4L2_MBUS_PARALLEL: 18787d459937SAndrzej Hajda break; 18797d459937SAndrzej Hajda default: 18807d459937SAndrzej Hajda dev_err(dev, "unsupported bus in endpoint defined at node %s\n", 18817d459937SAndrzej Hajda node->full_name); 18827d459937SAndrzej Hajda return -EINVAL; 18837d459937SAndrzej Hajda } 18847d459937SAndrzej Hajda 18857d459937SAndrzej Hajda return 0; 18867d459937SAndrzej Hajda } 18877d459937SAndrzej Hajda 18887d459937SAndrzej Hajda static int s5k5baf_configure_subdevs(struct s5k5baf *state, 18897d459937SAndrzej Hajda struct i2c_client *c) 18907d459937SAndrzej Hajda { 18917d459937SAndrzej Hajda struct v4l2_subdev *sd; 18927d459937SAndrzej Hajda int ret; 18937d459937SAndrzej Hajda 18947d459937SAndrzej Hajda sd = &state->cis_sd; 18957d459937SAndrzej Hajda v4l2_subdev_init(sd, &s5k5baf_cis_subdev_ops); 18967d459937SAndrzej Hajda sd->owner = THIS_MODULE; 18977d459937SAndrzej Hajda v4l2_set_subdevdata(sd, state); 18987d459937SAndrzej Hajda snprintf(sd->name, sizeof(sd->name), "S5K5BAF-CIS %d-%04x", 18997d459937SAndrzej Hajda i2c_adapter_id(c->adapter), c->addr); 19007d459937SAndrzej Hajda 19017d459937SAndrzej Hajda sd->internal_ops = &s5k5baf_cis_subdev_internal_ops; 19027d459937SAndrzej Hajda sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; 19037d459937SAndrzej Hajda 19047d459937SAndrzej Hajda state->cis_pad.flags = MEDIA_PAD_FL_SOURCE; 19057d459937SAndrzej Hajda sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV_SENSOR; 19067d459937SAndrzej Hajda ret = media_entity_init(&sd->entity, NUM_CIS_PADS, &state->cis_pad, 0); 19077d459937SAndrzej Hajda if (ret < 0) 19087d459937SAndrzej Hajda goto err; 19097d459937SAndrzej Hajda 19107d459937SAndrzej Hajda sd = &state->sd; 19117d459937SAndrzej Hajda v4l2_i2c_subdev_init(sd, c, &s5k5baf_subdev_ops); 19127d459937SAndrzej Hajda snprintf(sd->name, sizeof(sd->name), "S5K5BAF-ISP %d-%04x", 19137d459937SAndrzej Hajda i2c_adapter_id(c->adapter), c->addr); 19147d459937SAndrzej Hajda 19157d459937SAndrzej Hajda sd->internal_ops = &s5k5baf_subdev_internal_ops; 19167d459937SAndrzej Hajda sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; 19177d459937SAndrzej Hajda 19187d459937SAndrzej Hajda state->pads[PAD_CIS].flags = MEDIA_PAD_FL_SINK; 19197d459937SAndrzej Hajda state->pads[PAD_OUT].flags = MEDIA_PAD_FL_SOURCE; 19207d459937SAndrzej Hajda sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV; 19217d459937SAndrzej Hajda ret = media_entity_init(&sd->entity, NUM_ISP_PADS, state->pads, 0); 19227d459937SAndrzej Hajda 19237d459937SAndrzej Hajda if (!ret) 19247d459937SAndrzej Hajda return 0; 19257d459937SAndrzej Hajda 19267d459937SAndrzej Hajda media_entity_cleanup(&state->cis_sd.entity); 19277d459937SAndrzej Hajda err: 19287d459937SAndrzej Hajda dev_err(&c->dev, "cannot init media entity %s\n", sd->name); 19297d459937SAndrzej Hajda return ret; 19307d459937SAndrzej Hajda } 19317d459937SAndrzej Hajda 19327d459937SAndrzej Hajda static int s5k5baf_configure_regulators(struct s5k5baf *state) 19337d459937SAndrzej Hajda { 19347d459937SAndrzej Hajda struct i2c_client *c = v4l2_get_subdevdata(&state->sd); 19357d459937SAndrzej Hajda int ret; 19367d459937SAndrzej Hajda int i; 19377d459937SAndrzej Hajda 19387d459937SAndrzej Hajda for (i = 0; i < S5K5BAF_NUM_SUPPLIES; i++) 19397d459937SAndrzej Hajda state->supplies[i].supply = s5k5baf_supply_names[i]; 19407d459937SAndrzej Hajda 19417d459937SAndrzej Hajda ret = devm_regulator_bulk_get(&c->dev, S5K5BAF_NUM_SUPPLIES, 19427d459937SAndrzej Hajda state->supplies); 19437d459937SAndrzej Hajda if (ret < 0) 19447d459937SAndrzej Hajda v4l2_err(c, "failed to get regulators\n"); 19457d459937SAndrzej Hajda return ret; 19467d459937SAndrzej Hajda } 19477d459937SAndrzej Hajda 19487d459937SAndrzej Hajda static int s5k5baf_probe(struct i2c_client *c, 19497d459937SAndrzej Hajda const struct i2c_device_id *id) 19507d459937SAndrzej Hajda { 19517d459937SAndrzej Hajda struct s5k5baf *state; 19527d459937SAndrzej Hajda int ret; 19537d459937SAndrzej Hajda 19547d459937SAndrzej Hajda state = devm_kzalloc(&c->dev, sizeof(*state), GFP_KERNEL); 19557d459937SAndrzej Hajda if (!state) 19567d459937SAndrzej Hajda return -ENOMEM; 19577d459937SAndrzej Hajda 19587d459937SAndrzej Hajda mutex_init(&state->lock); 19597d459937SAndrzej Hajda state->crop_sink = s5k5baf_cis_rect; 19607d459937SAndrzej Hajda state->compose = s5k5baf_cis_rect; 19617d459937SAndrzej Hajda state->crop_source = s5k5baf_cis_rect; 19627d459937SAndrzej Hajda 19637d459937SAndrzej Hajda ret = s5k5baf_parse_device_node(state, &c->dev); 19647d459937SAndrzej Hajda if (ret < 0) 19657d459937SAndrzej Hajda return ret; 19667d459937SAndrzej Hajda 19677d459937SAndrzej Hajda ret = s5k5baf_configure_subdevs(state, c); 19687d459937SAndrzej Hajda if (ret < 0) 19697d459937SAndrzej Hajda return ret; 19707d459937SAndrzej Hajda 19717d459937SAndrzej Hajda ret = s5k5baf_configure_gpios(state); 19727d459937SAndrzej Hajda if (ret < 0) 19737d459937SAndrzej Hajda goto err_me; 19747d459937SAndrzej Hajda 19757d459937SAndrzej Hajda ret = s5k5baf_configure_regulators(state); 19767d459937SAndrzej Hajda if (ret < 0) 19777d459937SAndrzej Hajda goto err_me; 19787d459937SAndrzej Hajda 19797d459937SAndrzej Hajda state->clock = devm_clk_get(state->sd.dev, S5K5BAF_CLK_NAME); 19807d459937SAndrzej Hajda if (IS_ERR(state->clock)) { 19817d459937SAndrzej Hajda ret = -EPROBE_DEFER; 19827d459937SAndrzej Hajda goto err_me; 19837d459937SAndrzej Hajda } 19847d459937SAndrzej Hajda 19857d459937SAndrzej Hajda ret = s5k5baf_power_on(state); 19867d459937SAndrzej Hajda if (ret < 0) { 19877d459937SAndrzej Hajda ret = -EPROBE_DEFER; 19887d459937SAndrzej Hajda goto err_me; 19897d459937SAndrzej Hajda } 19907d459937SAndrzej Hajda s5k5baf_hw_init(state); 19917d459937SAndrzej Hajda ret = s5k5baf_check_fw_revision(state); 19927d459937SAndrzej Hajda 19937d459937SAndrzej Hajda s5k5baf_power_off(state); 19947d459937SAndrzej Hajda if (ret < 0) 19957d459937SAndrzej Hajda goto err_me; 19967d459937SAndrzej Hajda 19977d459937SAndrzej Hajda ret = s5k5baf_initialize_ctrls(state); 19987d459937SAndrzej Hajda if (ret < 0) 19997d459937SAndrzej Hajda goto err_me; 20007d459937SAndrzej Hajda 20017d459937SAndrzej Hajda ret = v4l2_async_register_subdev(&state->sd); 20027d459937SAndrzej Hajda if (ret < 0) 20037d459937SAndrzej Hajda goto err_ctrl; 20047d459937SAndrzej Hajda 20057d459937SAndrzej Hajda return 0; 20067d459937SAndrzej Hajda 20077d459937SAndrzej Hajda err_ctrl: 20087d459937SAndrzej Hajda v4l2_ctrl_handler_free(state->sd.ctrl_handler); 20097d459937SAndrzej Hajda err_me: 20107d459937SAndrzej Hajda media_entity_cleanup(&state->sd.entity); 20117d459937SAndrzej Hajda media_entity_cleanup(&state->cis_sd.entity); 20127d459937SAndrzej Hajda return ret; 20137d459937SAndrzej Hajda } 20147d459937SAndrzej Hajda 20157d459937SAndrzej Hajda static int s5k5baf_remove(struct i2c_client *c) 20167d459937SAndrzej Hajda { 20177d459937SAndrzej Hajda struct v4l2_subdev *sd = i2c_get_clientdata(c); 20187d459937SAndrzej Hajda struct s5k5baf *state = to_s5k5baf(sd); 20197d459937SAndrzej Hajda 20207d459937SAndrzej Hajda v4l2_async_unregister_subdev(sd); 20217d459937SAndrzej Hajda v4l2_ctrl_handler_free(sd->ctrl_handler); 20227d459937SAndrzej Hajda media_entity_cleanup(&sd->entity); 20237d459937SAndrzej Hajda 20247d459937SAndrzej Hajda sd = &state->cis_sd; 20257d459937SAndrzej Hajda v4l2_device_unregister_subdev(sd); 20267d459937SAndrzej Hajda media_entity_cleanup(&sd->entity); 20277d459937SAndrzej Hajda 20287d459937SAndrzej Hajda return 0; 20297d459937SAndrzej Hajda } 20307d459937SAndrzej Hajda 20317d459937SAndrzej Hajda static const struct i2c_device_id s5k5baf_id[] = { 20327d459937SAndrzej Hajda { S5K5BAF_DRIVER_NAME, 0 }, 20337d459937SAndrzej Hajda { }, 20347d459937SAndrzej Hajda }; 20357d459937SAndrzej Hajda MODULE_DEVICE_TABLE(i2c, s5k5baf_id); 20367d459937SAndrzej Hajda 20377d459937SAndrzej Hajda static const struct of_device_id s5k5baf_of_match[] = { 20387d459937SAndrzej Hajda { .compatible = "samsung,s5k5baf" }, 20397d459937SAndrzej Hajda { } 20407d459937SAndrzej Hajda }; 20417d459937SAndrzej Hajda MODULE_DEVICE_TABLE(of, s5k5baf_of_match); 20427d459937SAndrzej Hajda 20437d459937SAndrzej Hajda static struct i2c_driver s5k5baf_i2c_driver = { 20447d459937SAndrzej Hajda .driver = { 20457d459937SAndrzej Hajda .of_match_table = s5k5baf_of_match, 20467d459937SAndrzej Hajda .name = S5K5BAF_DRIVER_NAME 20477d459937SAndrzej Hajda }, 20487d459937SAndrzej Hajda .probe = s5k5baf_probe, 20497d459937SAndrzej Hajda .remove = s5k5baf_remove, 20507d459937SAndrzej Hajda .id_table = s5k5baf_id, 20517d459937SAndrzej Hajda }; 20527d459937SAndrzej Hajda 20537d459937SAndrzej Hajda module_i2c_driver(s5k5baf_i2c_driver); 20547d459937SAndrzej Hajda 20557d459937SAndrzej Hajda MODULE_DESCRIPTION("Samsung S5K5BAF(X) UXGA camera driver"); 20567d459937SAndrzej Hajda MODULE_AUTHOR("Andrzej Hajda <a.hajda@samsung.com>"); 20577d459937SAndrzej Hajda MODULE_LICENSE("GPL v2"); 2058