// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (C) 2011-2013 Freescale Semiconductor, Inc. All Rights Reserved. * Copyright (C) 2014-2017 Mentor Graphics Inc. * Copyright (C) 2021 StarFive Technology Co., Ltd. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "stfcamss.h" /* min/typical/max system clock (xclk) frequencies */ #define SC2235_XCLK_MIN 6000000 #define SC2235_XCLK_MAX 54000000 #define SC2235_CHIP_ID (0x2235) #define SC2235_REG_CHIP_ID 0x3107 #define SC2235_REG_AEC_PK_MANUAL 0x3e03 #define SC2235_REG_AEC_PK_EXPOSURE_HI 0x3e01 #define SC2235_REG_AEC_PK_EXPOSURE_LO 0x3e02 #define SC2235_REG_AEC_PK_REAL_GAIN 0x3e08 #define SC2235_REG_TIMING_HTS 0x320c #define SC2235_REG_TIMING_VTS 0x320e #define SC2235_REG_TEST_SET0 0x4501 #define SC2235_REG_TEST_SET1 0x3902 #define SC2235_REG_TIMING_TC_REG21 0x3221 #define SC2235_REG_SC_PLL_CTRL0 0x3039 #define SC2235_REG_SC_PLL_CTRL1 0x303a enum sc2235_mode_id { SC2235_MODE_1080P_1920_1080 = 0, SC2235_NUM_MODES, }; enum sc2235_frame_rate { SC2235_15_FPS = 0, SC2235_30_FPS, SC2235_60_FPS, SC2235_NUM_FRAMERATES, }; struct sc2235_pixfmt { u32 code; u32 colorspace; }; static const struct sc2235_pixfmt sc2235_formats[] = { { MEDIA_BUS_FMT_SGBRG10_1X10, V4L2_COLORSPACE_SRGB, }, }; static const int sc2235_framerates[] = { [SC2235_15_FPS] = 15, [SC2235_30_FPS] = 30, [SC2235_60_FPS] = 60, }; /* regulator supplies */ static const char * const sc2235_supply_name[] = { "DOVDD", /* Digital I/O (1.8V) supply */ "AVDD", /* Analog (2.8V) supply */ "DVDD", /* Digital Core (1.5V) supply */ }; #define SC2235_NUM_SUPPLIES ARRAY_SIZE(sc2235_supply_name) struct reg_value { u16 reg_addr; u8 val; u8 mask; u32 delay_ms; }; struct sc2235_mode_info { enum sc2235_mode_id id; u32 hact; u32 htot; u32 vact; u32 vtot; const struct reg_value *reg_data; u32 reg_data_size; u32 max_fps; }; struct sc2235_ctrls { struct v4l2_ctrl_handler handler; struct v4l2_ctrl *pixel_rate; struct { struct v4l2_ctrl *auto_exp; struct v4l2_ctrl *exposure; }; struct { struct v4l2_ctrl *auto_wb; struct v4l2_ctrl *blue_balance; struct v4l2_ctrl *red_balance; }; struct { struct v4l2_ctrl *auto_gain; struct v4l2_ctrl *gain; }; struct v4l2_ctrl *brightness; struct v4l2_ctrl *light_freq; struct v4l2_ctrl *saturation; struct v4l2_ctrl *contrast; struct v4l2_ctrl *hue; struct v4l2_ctrl *test_pattern; struct v4l2_ctrl *hflip; struct v4l2_ctrl *vflip; }; struct sc2235_dev { struct i2c_client *i2c_client; struct v4l2_subdev sd; struct media_pad pad; struct v4l2_fwnode_endpoint ep; /* the parsed DT endpoint info */ struct clk *xclk; /* system clock to SC2235 */ u32 xclk_freq; struct regulator_bulk_data supplies[SC2235_NUM_SUPPLIES]; struct gpio_desc *reset_gpio; struct gpio_desc *pwdn_gpio; bool upside_down; /* lock to protect all members below */ struct mutex lock; int power_count; struct v4l2_mbus_framefmt fmt; bool pending_fmt_change; const struct sc2235_mode_info *current_mode; const struct sc2235_mode_info *last_mode; enum sc2235_frame_rate current_fr; struct v4l2_fract frame_interval; struct sc2235_ctrls ctrls; u32 prev_sysclk, prev_hts; u32 ae_low, ae_high, ae_target; bool pending_mode_change; bool streaming; }; static inline struct sc2235_dev *to_sc2235_dev(struct v4l2_subdev *sd) { return container_of(sd, struct sc2235_dev, sd); } static inline struct v4l2_subdev *ctrl_to_sd(struct v4l2_ctrl *ctrl) { return &container_of(ctrl->handler, struct sc2235_dev, ctrls.handler)->sd; } /* sc2235 initial register */ static struct reg_value sc2235_init_regs_tbl_1080[] = { {0x0103, 0x01, 0, 0}, {0x0100, 0x00, 0, 0}, // {0x3039, 0x80, 0, 0}, {0x3621, 0x28, 0, 0}, {0x3309, 0x60, 0, 0}, {0x331f, 0x4d, 0, 0}, {0x3321, 0x4f, 0, 0}, {0x33b5, 0x10, 0, 0}, {0x3303, 0x20, 0, 0}, {0x331e, 0x0d, 0, 0}, {0x3320, 0x0f, 0, 0}, {0x3622, 0x02, 0, 0}, {0x3633, 0x42, 0, 0}, {0x3634, 0x42, 0, 0}, {0x3306, 0x66, 0, 0}, {0x330b, 0xd1, 0, 0}, {0x3301, 0x0e, 0, 0}, {0x320c, 0x08, 0, 0}, {0x320d, 0x98, 0, 0}, {0x3364, 0x05, 0, 0}, // [2] 1: write at sampling ending {0x363c, 0x28, 0, 0}, //bypass nvdd {0x363b, 0x0a, 0, 0}, //HVDD {0x3635, 0xa0, 0, 0}, //TXVDD {0x4500, 0x59, 0, 0}, {0x3d08, 0x02, 0, 0}, {0x3908, 0x11, 0, 0}, {0x363c, 0x08, 0, 0}, {0x3e03, 0x03, 0, 0}, {0x3e01, 0x46, 0, 0}, //0703 {0x3381, 0x0a, 0, 0}, {0x3348, 0x09, 0, 0}, {0x3349, 0x50, 0, 0}, {0x334a, 0x02, 0, 0}, {0x334b, 0x60, 0, 0}, {0x3380, 0x04, 0, 0}, {0x3340, 0x06, 0, 0}, {0x3341, 0x50, 0, 0}, {0x3342, 0x02, 0, 0}, {0x3343, 0x60, 0, 0}, //0707 {0x3632, 0x88, 0, 0}, //anti sm {0x3309, 0xa0, 0, 0}, {0x331f, 0x8d, 0, 0}, {0x3321, 0x8f, 0, 0}, {0x335e, 0x01, 0, 0}, //ana dithering {0x335f, 0x03, 0, 0}, {0x337c, 0x04, 0, 0}, {0x337d, 0x06, 0, 0}, {0x33a0, 0x05, 0, 0}, {0x3301, 0x05, 0, 0}, {0x337f, 0x03, 0, 0}, {0x3368, 0x02, 0, 0}, {0x3369, 0x00, 0, 0}, {0x336a, 0x00, 0, 0}, {0x336b, 0x00, 0, 0}, {0x3367, 0x08, 0, 0}, {0x330e, 0x30, 0, 0}, {0x3366, 0x7c, 0, 0}, // div_rst gap {0x3635, 0xc1, 0, 0}, {0x363b, 0x09, 0, 0}, {0x363c, 0x07, 0, 0}, {0x391e, 0x00, 0, 0}, {0x3637, 0x14, 0, 0}, //fullwell 7K {0x3306, 0x54, 0, 0}, {0x330b, 0xd8, 0, 0}, {0x366e, 0x08, 0, 0}, // ofs auto en [3] {0x366f, 0x2f, 0, 0}, {0x3631, 0x84, 0, 0}, {0x3630, 0x48, 0, 0}, {0x3622, 0x06, 0, 0}, //ramp by sc {0x3638, 0x1f, 0, 0}, {0x3625, 0x02, 0, 0}, {0x3636, 0x24, 0, 0}, //0714 {0x3348, 0x08, 0, 0}, {0x3e03, 0x0b, 0, 0}, //7.17 fpn {0x3342, 0x03, 0, 0}, {0x3343, 0xa0, 0, 0}, {0x334a, 0x03, 0, 0}, {0x334b, 0xa0, 0, 0}, //0718 {0x3343, 0xb0, 0, 0}, {0x334b, 0xb0, 0, 0}, //0720 //digital ctr, 0, 0l {0x3802, 0x01, 0, 0}, {0x3235, 0x04, 0, 0}, {0x3236, 0x63, 0, 0}, // vts-2 //fpn {0x3343, 0xd0, 0, 0}, {0x334b, 0xd0, 0, 0}, {0x3348, 0x07, 0, 0}, {0x3349, 0x80, 0, 0}, //0724 {0x391b, 0x4d, 0, 0}, {0x3342, 0x04, 0, 0}, {0x3343, 0x20, 0, 0}, {0x334a, 0x04, 0, 0}, {0x334b, 0x20, 0, 0}, //0804 {0x3222, 0x29, 0, 0}, {0x3901, 0x02, 0, 0}, //0808 //digital ctr, 0, 0l {0x3f00, 0x07, 0, 0}, // bit[2] = 1 {0x3f04, 0x08, 0, 0}, {0x3f05, 0x74, 0, 0}, // hts - { 0x24 //0809 {0x330b, 0xc8, 0, 0}, //0817 {0x3306, 0x4a, 0, 0}, {0x330b, 0xca, 0, 0}, {0x3639, 0x09, 0, 0}, //manual DPC {0x5780, 0xff, 0, 0}, {0x5781, 0x04, 0, 0}, {0x5785, 0x18, 0, 0}, //0822 // {0x3039, 0x35, 0, 0}, //fps // {0x303a, 0x2e, 0, 0}, {0x3034, 0x05, 0, 0}, {0x3035, 0x2a, 0, 0}, {0x320c, 0x08, 0, 0}, {0x320d, 0xca, 0, 0}, {0x320e, 0x04, 0, 0}, {0x320f, 0xb0, 0, 0}, {0x3f04, 0x08, 0, 0}, {0x3f05, 0xa6, 0, 0}, // hts - { 0x24 {0x3235, 0x04, 0, 0}, {0x3236, 0xae, 0, 0}, // vts-2 //0825 {0x3313, 0x05, 0, 0}, {0x3678, 0x42, 0, 0}, //for AE control per frame {0x3670, 0x00, 0, 0}, {0x3633, 0x42, 0, 0}, {0x3802, 0x00, 0, 0}, //20180126 {0x3677, 0x3f, 0, 0}, {0x3306, 0x44, 0, 0}, //20180126[3c },4a] {0x330b, 0xca, 0, 0}, //20180126[c2 },d3] //20180202 {0x3237, 0x08, 0, 0}, {0x3238, 0x9a, 0, 0}, //hts-0x30 //20180417 {0x3640, 0x01, 0, 0}, {0x3641, 0x02, 0, 0}, {0x3301, 0x12, 0, 0}, //[8 },15]20180126 {0x3631, 0x84, 0, 0}, {0x366f, 0x2f, 0, 0}, {0x3622, 0xc6, 0, 0}, //20180117 {0x0100, 0x01, 0, 0}, //{ 0x4501, 0xc8, 0, 0}, //bar testing //{ 0x3902, 0x45, 0, 0}, }; static struct reg_value sc2235_setting_1080P_1920_1080[] = { }; /* power-on sensor init reg table */ static const struct sc2235_mode_info sc2235_mode_init_data = { SC2235_MODE_1080P_1920_1080, 1920, 0x8ca, 1080, 0x4b0, sc2235_init_regs_tbl_1080, ARRAY_SIZE(sc2235_init_regs_tbl_1080), SC2235_60_FPS, }; static const struct sc2235_mode_info sc2235_mode_data[SC2235_NUM_MODES] = { {SC2235_MODE_1080P_1920_1080, 1920, 0x8ca, 1080, 0x4b0, sc2235_setting_1080P_1920_1080, ARRAY_SIZE(sc2235_setting_1080P_1920_1080), SC2235_60_FPS}, }; static int sc2235_write_reg(struct sc2235_dev *sensor, u16 reg, u8 val) { struct i2c_client *client = sensor->i2c_client; struct i2c_msg msg; u8 buf[3]; int ret; buf[0] = reg >> 8; buf[1] = reg & 0xff; buf[2] = val; msg.addr = client->addr; msg.flags = client->flags; msg.buf = buf; msg.len = sizeof(buf); ret = i2c_transfer(client->adapter, &msg, 1); if (ret < 0) { dev_err(&client->dev, "%s: error: reg=%x, val=%x\n", __func__, reg, val); return ret; } return 0; } static int sc2235_read_reg(struct sc2235_dev *sensor, u16 reg, u8 *val) { struct i2c_client *client = sensor->i2c_client; struct i2c_msg msg[2]; u8 buf[2]; int ret; buf[0] = reg >> 8; buf[1] = reg & 0xff; msg[0].addr = client->addr; msg[0].flags = client->flags; msg[0].buf = buf; msg[0].len = sizeof(buf); msg[1].addr = client->addr; msg[1].flags = client->flags | I2C_M_RD; msg[1].buf = buf; msg[1].len = 1; ret = i2c_transfer(client->adapter, msg, 2); if (ret < 0) { dev_err(&client->dev, "%s: error: reg=%x\n", __func__, reg); return ret; } *val = buf[0]; return 0; } static int sc2235_read_reg16(struct sc2235_dev *sensor, u16 reg, u16 *val) { u8 hi, lo; int ret; ret = sc2235_read_reg(sensor, reg, &hi); if (ret) return ret; ret = sc2235_read_reg(sensor, reg + 1, &lo); if (ret) return ret; *val = ((u16)hi << 8) | (u16)lo; return 0; } static int sc2235_write_reg16(struct sc2235_dev *sensor, u16 reg, u16 val) { int ret; ret = sc2235_write_reg(sensor, reg, val >> 8); if (ret) return ret; return sc2235_write_reg(sensor, reg + 1, val & 0xff); } static int sc2235_mod_reg(struct sc2235_dev *sensor, u16 reg, u8 mask, u8 val) { u8 readval; int ret; ret = sc2235_read_reg(sensor, reg, &readval); if (ret) return ret; readval &= ~mask; val &= mask; val |= readval; return sc2235_write_reg(sensor, reg, val); } #define SC2235_PLL_PREDIV 3 #define SC2235_SYSDIV_MIN 0 #define SC2235_SYSDIV_MAX 7 #define SC2235_PLL_MULT_MIN 0 #define SC2235_PLL_MULT_MAX 63 static unsigned long sc2235_compute_sys_clk(struct sc2235_dev *sensor, u8 pll_pre, u8 pll_mult, u8 sysdiv) { unsigned long sysclk = sensor->xclk_freq * (64 - pll_mult) / (pll_pre * (sysdiv + 1)); /* PLL1 output cannot exceed 1GHz. */ if (sysclk / 1000000 > 1000) return 0; return sysclk; } static unsigned long sc2235_calc_sys_clk(struct sc2235_dev *sensor, unsigned long rate, u8 *pll_prediv, u8 *pll_mult, u8 *sysdiv) { unsigned long best = ~0; u8 best_sysdiv = 1, best_mult = 1; u8 _sysdiv, _pll_mult; for (_sysdiv = SC2235_SYSDIV_MIN; _sysdiv <= SC2235_SYSDIV_MAX; _sysdiv++) { for (_pll_mult = SC2235_PLL_MULT_MIN; _pll_mult <= SC2235_PLL_MULT_MAX; _pll_mult++) { unsigned long _rate; _rate = sc2235_compute_sys_clk(sensor, SC2235_PLL_PREDIV, _pll_mult, _sysdiv); /* * We have reached the maximum allowed PLL1 output, * increase sysdiv. */ if (!_rate) break; /* * Prefer rates above the expected clock rate than * below, even if that means being less precise. */ if (_rate < rate) continue; if (abs(rate - _rate) < abs(rate - best)) { best = _rate; best_sysdiv = _sysdiv; best_mult = _pll_mult; } if (_rate == rate) goto out; } } out: *sysdiv = best_sysdiv; *pll_prediv = SC2235_PLL_PREDIV; *pll_mult = best_mult; return best; } static int sc2235_set_timings(struct sc2235_dev *sensor, const struct sc2235_mode_info *mode) { int ret = 0; return ret; } static int sc2235_load_regs(struct sc2235_dev *sensor, const struct sc2235_mode_info *mode) { const struct reg_value *regs = mode->reg_data; unsigned int i; u32 delay_ms; u16 reg_addr; u8 mask, val; int ret = 0; st_info(ST_SENSOR, "%s, mode = 0x%x\n", __func__, mode->id); for (i = 0; i < mode->reg_data_size; ++i, ++regs) { delay_ms = regs->delay_ms; reg_addr = regs->reg_addr; val = regs->val; mask = regs->mask; if (mask) ret = sc2235_mod_reg(sensor, reg_addr, mask, val); else ret = sc2235_write_reg(sensor, reg_addr, val); if (ret) break; if (delay_ms) usleep_range(1000 * delay_ms, 1000 * delay_ms + 100); } return sc2235_set_timings(sensor, mode); } static int sc2235_set_autoexposure(struct sc2235_dev *sensor, bool on) { return sc2235_mod_reg(sensor, SC2235_REG_AEC_PK_MANUAL, BIT(0), on ? 0 : BIT(0)); } static int sc2235_get_exposure(struct sc2235_dev *sensor) { int exp = 0, ret = 0; u8 temp; ret = sc2235_read_reg(sensor, SC2235_REG_AEC_PK_EXPOSURE_HI, &temp); if (ret) return ret; exp |= (int)temp << 8; ret = sc2235_read_reg(sensor, SC2235_REG_AEC_PK_EXPOSURE_LO, &temp); if (ret) return ret; exp |= (int)temp; return exp >> 4; } static int sc2235_set_exposure(struct sc2235_dev *sensor, u32 exposure) { int ret; exposure <<= 4; ret = sc2235_write_reg(sensor, SC2235_REG_AEC_PK_EXPOSURE_LO, exposure & 0xff); if (ret) return ret; return sc2235_write_reg(sensor, SC2235_REG_AEC_PK_EXPOSURE_HI, (exposure >> 8) & 0xff); } static int sc2235_get_gain(struct sc2235_dev *sensor) { u16 gain; int ret; ret = sc2235_read_reg16(sensor, SC2235_REG_AEC_PK_REAL_GAIN, &gain); if (ret) return ret; return gain & 0x1fff; } static int sc2235_set_gain(struct sc2235_dev *sensor, int gain) { return sc2235_write_reg16(sensor, SC2235_REG_AEC_PK_REAL_GAIN, (u16)gain & 0x1fff); } static int sc2235_set_autogain(struct sc2235_dev *sensor, bool on) { return sc2235_mod_reg(sensor, SC2235_REG_AEC_PK_MANUAL, BIT(1), on ? 0 : BIT(1)); } static int sc2235_set_stream_dvp(struct sc2235_dev *sensor, bool on) { return 0; } static int sc2235_get_sysclk(struct sc2235_dev *sensor) { return 0; } static int sc2235_set_night_mode(struct sc2235_dev *sensor) { return 0; } static int sc2235_get_hts(struct sc2235_dev *sensor) { u16 hts; int ret; ret = sc2235_read_reg16(sensor, SC2235_REG_TIMING_HTS, &hts); if (ret) return ret; return hts; } static int sc2235_get_vts(struct sc2235_dev *sensor) { u16 vts; int ret; ret = sc2235_read_reg16(sensor, SC2235_REG_TIMING_VTS, &vts); if (ret) return ret; return vts; } static int sc2235_set_vts(struct sc2235_dev *sensor, int vts) { return sc2235_write_reg16(sensor, SC2235_REG_TIMING_VTS, vts); } static int sc2235_get_light_freq(struct sc2235_dev *sensor) { return 0; } static int sc2235_set_bandingfilter(struct sc2235_dev *sensor) { return 0; } static int sc2235_set_ae_target(struct sc2235_dev *sensor, int target) { return 0; } static int sc2235_get_binning(struct sc2235_dev *sensor) { return 0; } static int sc2235_set_binning(struct sc2235_dev *sensor, bool enable) { return 0; } static const struct sc2235_mode_info * sc2235_find_mode(struct sc2235_dev *sensor, enum sc2235_frame_rate fr, int width, int height, bool nearest) { const struct sc2235_mode_info *mode; mode = v4l2_find_nearest_size(sc2235_mode_data, ARRAY_SIZE(sc2235_mode_data), hact, vact, width, height); if (!mode || (!nearest && (mode->hact != width || mode->vact != height))) return NULL; /* Check to see if the current mode exceeds the max frame rate */ if (sc2235_framerates[fr] > sc2235_framerates[mode->max_fps]) return NULL; return mode; } static u64 sc2235_calc_pixel_rate(struct sc2235_dev *sensor) { u64 rate; rate = sensor->current_mode->vtot * sensor->current_mode->htot; rate *= sc2235_framerates[sensor->current_fr]; return rate; } /* * sc2235_set_dvp_pclk() - Calculate the clock tree configuration values * for the dvp output. * * @rate: The requested bandwidth per lane in bytes per second. * 'Bandwidth Per Lane' is calculated as: * rate = HTOT * VTOT * FPS; * * This function use the requested bandwidth to calculate: * - rate = xclk * (64 - M) / (N * (S + 1)); * */ #define PLL_PREDIV 1 #define PLL_SYSEL 0 static int sc2235_set_dvp_pclk(struct sc2235_dev *sensor, unsigned long rate) { const struct sc2235_mode_info *mode = sensor->current_mode; const struct sc2235_mode_info *orig_mode = sensor->last_mode; u8 prediv, mult, sysdiv; int ret = 0; sc2235_calc_sys_clk(sensor, rate, &prediv, &mult, &sysdiv); st_info(ST_SENSOR, "%s, prediv = %d, mult = %d, sysdiv = %d\n", __func__, prediv, mult, sysdiv); ret = sc2235_mod_reg(sensor, SC2235_REG_SC_PLL_CTRL0, 0x7f, (sysdiv << 4) | (prediv << 1) | ((mult & 0x20) >> 5)); if (ret) return ret; return sc2235_mod_reg(sensor, SC2235_REG_SC_PLL_CTRL1, 0xf8, mult << 3); } /* * if sensor changes inside scaling or subsampling * change mode directly */ static int sc2235_set_mode_direct(struct sc2235_dev *sensor, const struct sc2235_mode_info *mode) { if (!mode->reg_data) return -EINVAL; /* Write capture setting */ return sc2235_load_regs(sensor, mode); } static int sc2235_set_mode(struct sc2235_dev *sensor) { const struct sc2235_mode_info *mode = sensor->current_mode; const struct sc2235_mode_info *orig_mode = sensor->last_mode; bool auto_gain = sensor->ctrls.auto_gain->val == 1; bool auto_exp = sensor->ctrls.auto_exp->val == V4L2_EXPOSURE_AUTO; unsigned long rate; int ret = 0; /* auto gain and exposure must be turned off when changing modes */ if (auto_gain) { ret = sc2235_set_autogain(sensor, false); if (ret) return ret; } if (auto_exp) { ret = sc2235_set_autoexposure(sensor, false); if (ret) goto restore_auto_gain; } rate = sc2235_calc_pixel_rate(sensor); if (sensor->ep.bus_type == V4L2_MBUS_PARALLEL) ret = sc2235_set_dvp_pclk(sensor, rate); if (ret < 0) return 0; ret = sc2235_set_mode_direct(sensor, mode); if (ret < 0) goto restore_auto_exp_gain; /* restore auto gain and exposure */ if (auto_gain) sc2235_set_autogain(sensor, true); if (auto_exp) sc2235_set_autoexposure(sensor, true); sensor->pending_mode_change = false; sensor->last_mode = mode; return 0; restore_auto_exp_gain: if (auto_exp) sc2235_set_autoexposure(sensor, true); restore_auto_gain: if (auto_gain) sc2235_set_autogain(sensor, true); return ret; } static int sc2235_set_framefmt(struct sc2235_dev *sensor, struct v4l2_mbus_framefmt *format); /* restore the last set video mode after chip power-on */ static int sc2235_restore_mode(struct sc2235_dev *sensor) { int ret; /* first load the initial register values */ ret = sc2235_load_regs(sensor, &sc2235_mode_init_data); if (ret < 0) return ret; sensor->last_mode = &sc2235_mode_init_data; /* now restore the last capture mode */ ret = sc2235_set_mode(sensor); if (ret < 0) return ret; return sc2235_set_framefmt(sensor, &sensor->fmt); } static void sc2235_power(struct sc2235_dev *sensor, bool enable) { if (!sensor->pwdn_gpio) return; gpiod_set_value_cansleep(sensor->pwdn_gpio, enable ? 0 : 1); } static void sc2235_reset(struct sc2235_dev *sensor) { if (!sensor->reset_gpio) return; gpiod_set_value_cansleep(sensor->reset_gpio, 0); /* camera power cycle */ sc2235_power(sensor, false); usleep_range(5000, 10000); sc2235_power(sensor, true); usleep_range(5000, 10000); gpiod_set_value_cansleep(sensor->reset_gpio, 1); usleep_range(1000, 2000); gpiod_set_value_cansleep(sensor->reset_gpio, 0); usleep_range(20000, 25000); } static int sc2235_set_power_on(struct sc2235_dev *sensor) { struct i2c_client *client = sensor->i2c_client; int ret; ret = clk_prepare_enable(sensor->xclk); if (ret) { dev_err(&client->dev, "%s: failed to enable clock\n", __func__); return ret; } ret = regulator_bulk_enable(SC2235_NUM_SUPPLIES, sensor->supplies); if (ret) { dev_err(&client->dev, "%s: failed to enable regulators\n", __func__); goto xclk_off; } sc2235_reset(sensor); sc2235_power(sensor, true); return 0; xclk_off: clk_disable_unprepare(sensor->xclk); return ret; } static void sc2235_set_power_off(struct sc2235_dev *sensor) { sc2235_power(sensor, false); regulator_bulk_disable(SC2235_NUM_SUPPLIES, sensor->supplies); clk_disable_unprepare(sensor->xclk); } static int sc2235_set_power_dvp(struct sc2235_dev *sensor, bool on) { unsigned int flags = sensor->ep.bus.parallel.flags; bool bt656 = sensor->ep.bus_type == V4L2_MBUS_BT656; u8 polarities = 0; int ret; /* * configure parallel port control lines polarity * * POLARITY CTRL0 * - [5]: PCLK polarity (0: active low, 1: active high) * - [1]: HREF polarity (0: active low, 1: active high) * - [0]: VSYNC polarity (mismatch here between * datasheet and hardware, 0 is active high * and 1 is active low...) */ if (flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH) polarities |= BIT(1); if (flags & V4L2_MBUS_VSYNC_ACTIVE_LOW) polarities |= BIT(0); if (flags & V4L2_MBUS_PCLK_SAMPLE_RISING) polarities |= BIT(5); // ret = sc2235_write_reg(sensor, // SC2235_REG_POLARITY_CTRL00, // polarities); // if (ret) // return ret; return 0; } static int sc2235_set_power(struct sc2235_dev *sensor, bool on) { int ret = 0; if (on) { ret = sc2235_set_power_on(sensor); if (ret) return ret; ret = sc2235_restore_mode(sensor); if (ret) goto power_off; } if (sensor->ep.bus_type == V4L2_MBUS_PARALLEL) ret = sc2235_set_power_dvp(sensor, on); if (ret) goto power_off; if (!on) sc2235_set_power_off(sensor); return 0; power_off: sc2235_set_power_off(sensor); return ret; } static int sc2235_s_power(struct v4l2_subdev *sd, int on) { struct sc2235_dev *sensor = to_sc2235_dev(sd); int ret = 0; mutex_lock(&sensor->lock); /* * If the power count is modified from 0 to != 0 or from != 0 to 0, * update the power state. */ if (sensor->power_count == !on) { ret = sc2235_set_power(sensor, !!on); if (ret) goto out; } /* Update the power count. */ sensor->power_count += on ? 1 : -1; WARN_ON(sensor->power_count < 0); out: mutex_unlock(&sensor->lock); if (on && !ret && sensor->power_count == 1) { /* restore controls */ ret = v4l2_ctrl_handler_setup(&sensor->ctrls.handler); } return ret; } static int sc2235_try_frame_interval(struct sc2235_dev *sensor, struct v4l2_fract *fi, u32 width, u32 height) { const struct sc2235_mode_info *mode; enum sc2235_frame_rate rate = SC2235_15_FPS; int minfps, maxfps, best_fps, fps; int i; minfps = sc2235_framerates[SC2235_15_FPS]; maxfps = sc2235_framerates[SC2235_60_FPS]; if (fi->numerator == 0) { fi->denominator = maxfps; fi->numerator = 1; rate = SC2235_60_FPS; goto find_mode; } fps = clamp_val(DIV_ROUND_CLOSEST(fi->denominator, fi->numerator), minfps, maxfps); best_fps = minfps; for (i = 0; i < ARRAY_SIZE(sc2235_framerates); i++) { int curr_fps = sc2235_framerates[i]; if (abs(curr_fps - fps) < abs(best_fps - fps)) { best_fps = curr_fps; rate = i; } } fi->numerator = 1; fi->denominator = best_fps; find_mode: mode = sc2235_find_mode(sensor, rate, width, height, false); return mode ? rate : -EINVAL; } static int sc2235_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *format) { struct sc2235_dev *sensor = to_sc2235_dev(sd); struct v4l2_mbus_framefmt *fmt; if (format->pad != 0) return -EINVAL; mutex_lock(&sensor->lock); if (format->which == V4L2_SUBDEV_FORMAT_TRY) fmt = v4l2_subdev_get_try_format(&sensor->sd, cfg, format->pad); else fmt = &sensor->fmt; format->format = *fmt; mutex_unlock(&sensor->lock); return 0; } static int sc2235_try_fmt_internal(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *fmt, enum sc2235_frame_rate fr, const struct sc2235_mode_info **new_mode) { struct sc2235_dev *sensor = to_sc2235_dev(sd); const struct sc2235_mode_info *mode; int i; mode = sc2235_find_mode(sensor, fr, fmt->width, fmt->height, true); if (!mode) return -EINVAL; fmt->width = mode->hact; fmt->height = mode->vact; if (new_mode) *new_mode = mode; for (i = 0; i < ARRAY_SIZE(sc2235_formats); i++) if (sc2235_formats[i].code == fmt->code) break; if (i >= ARRAY_SIZE(sc2235_formats)) i = 0; fmt->code = sc2235_formats[i].code; fmt->colorspace = sc2235_formats[i].colorspace; fmt->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt->colorspace); fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE; fmt->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(fmt->colorspace); return 0; } static int sc2235_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *format) { struct sc2235_dev *sensor = to_sc2235_dev(sd); const struct sc2235_mode_info *new_mode; struct v4l2_mbus_framefmt *mbus_fmt = &format->format; struct v4l2_mbus_framefmt *fmt; int ret; if (format->pad != 0) return -EINVAL; mutex_lock(&sensor->lock); if (sensor->streaming) { ret = -EBUSY; goto out; } ret = sc2235_try_fmt_internal(sd, mbus_fmt, 0, &new_mode); if (ret) goto out; if (format->which == V4L2_SUBDEV_FORMAT_TRY) fmt = v4l2_subdev_get_try_format(sd, cfg, 0); else fmt = &sensor->fmt; if (mbus_fmt->code != sensor->fmt.code) sensor->pending_fmt_change = true; *fmt = *mbus_fmt; if (new_mode != sensor->current_mode) { sensor->current_mode = new_mode; sensor->pending_mode_change = true; } if (new_mode->max_fps < sensor->current_fr) { sensor->current_fr = new_mode->max_fps; sensor->frame_interval.numerator = 1; sensor->frame_interval.denominator = sc2235_framerates[sensor->current_fr]; sensor->current_mode = new_mode; sensor->pending_mode_change = true; } __v4l2_ctrl_s_ctrl_int64(sensor->ctrls.pixel_rate, sc2235_calc_pixel_rate(sensor)); out: mutex_unlock(&sensor->lock); return ret; } static int sc2235_set_framefmt(struct sc2235_dev *sensor, struct v4l2_mbus_framefmt *format) { int ret = 0; switch (format->code) { default: return ret; } return ret; } /* * Sensor Controls. */ static int sc2235_set_ctrl_hue(struct sc2235_dev *sensor, int value) { int ret = 0; return ret; } static int sc2235_set_ctrl_contrast(struct sc2235_dev *sensor, int value) { int ret = 0; return ret; } static int sc2235_set_ctrl_saturation(struct sc2235_dev *sensor, int value) { int ret = 0; return ret; } static int sc2235_set_ctrl_white_balance(struct sc2235_dev *sensor, int awb) { int ret = 0; return ret; } static int sc2235_set_ctrl_exposure(struct sc2235_dev *sensor, enum v4l2_exposure_auto_type auto_exposure) { struct sc2235_ctrls *ctrls = &sensor->ctrls; bool auto_exp = (auto_exposure == V4L2_EXPOSURE_AUTO); int ret = 0; if (ctrls->auto_exp->is_new) { ret = sc2235_set_autoexposure(sensor, auto_exp); if (ret) return ret; } if (!auto_exp && ctrls->exposure->is_new) { u16 max_exp = 0; ret = sc2235_get_vts(sensor); if (ret < 0) return ret; max_exp += ret - 4; ret = 0; if (ctrls->exposure->val < max_exp) ret = sc2235_set_exposure(sensor, ctrls->exposure->val); } return ret; } static int sc2235_set_ctrl_gain(struct sc2235_dev *sensor, bool auto_gain) { struct sc2235_ctrls *ctrls = &sensor->ctrls; int ret = 0; if (ctrls->auto_gain->is_new) { ret = sc2235_set_autogain(sensor, auto_gain); if (ret) return ret; } if (!auto_gain && ctrls->gain->is_new) ret = sc2235_set_gain(sensor, ctrls->gain->val); return ret; } static const char * const test_pattern_menu[] = { "Disabled", "Black bars", "Auto Black bars", }; #define SC2235_TEST_ENABLE BIT(3) #define SC2235_TEST_BLACK (3 << 0) static int sc2235_set_ctrl_test_pattern(struct sc2235_dev *sensor, int value) { int ret = 0; ret = sc2235_mod_reg(sensor, SC2235_REG_TEST_SET0, BIT(3), !!value << 3); ret |= sc2235_mod_reg(sensor, SC2235_REG_TEST_SET1, BIT(6), (value >> 1) << 6); return ret; } static int sc2235_set_ctrl_light_freq(struct sc2235_dev *sensor, int value) { return 0; } static int sc2235_set_ctrl_hflip(struct sc2235_dev *sensor, int value) { return sc2235_mod_reg(sensor, SC2235_REG_TIMING_TC_REG21, BIT(2) | BIT(1), (!(value ^ sensor->upside_down)) ? (BIT(2) | BIT(1)) : 0); } static int sc2235_set_ctrl_vflip(struct sc2235_dev *sensor, int value) { return sc2235_mod_reg(sensor, SC2235_REG_TIMING_TC_REG21, BIT(6) | BIT(5), (value ^ sensor->upside_down) ? (BIT(6) | BIT(5)) : 0); } static int sc2235_g_volatile_ctrl(struct v4l2_ctrl *ctrl) { struct v4l2_subdev *sd = ctrl_to_sd(ctrl); struct sc2235_dev *sensor = to_sc2235_dev(sd); int val; /* v4l2_ctrl_lock() locks our own mutex */ switch (ctrl->id) { case V4L2_CID_AUTOGAIN: val = sc2235_get_gain(sensor); if (val < 0) return val; sensor->ctrls.gain->val = val; break; case V4L2_CID_EXPOSURE_AUTO: val = sc2235_get_exposure(sensor); if (val < 0) return val; sensor->ctrls.exposure->val = val; break; } return 0; } static int sc2235_s_ctrl(struct v4l2_ctrl *ctrl) { struct v4l2_subdev *sd = ctrl_to_sd(ctrl); struct sc2235_dev *sensor = to_sc2235_dev(sd); int ret; /* v4l2_ctrl_lock() locks our own mutex */ /* * If the device is not powered up by the host driver do * not apply any controls to H/W at this time. Instead * the controls will be restored right after power-up. */ if (sensor->power_count == 0) return 0; switch (ctrl->id) { case V4L2_CID_AUTOGAIN: ret = sc2235_set_ctrl_gain(sensor, ctrl->val); break; case V4L2_CID_EXPOSURE_AUTO: ret = sc2235_set_ctrl_exposure(sensor, ctrl->val); break; case V4L2_CID_AUTO_WHITE_BALANCE: ret = sc2235_set_ctrl_white_balance(sensor, ctrl->val); break; case V4L2_CID_HUE: ret = sc2235_set_ctrl_hue(sensor, ctrl->val); break; case V4L2_CID_CONTRAST: ret = sc2235_set_ctrl_contrast(sensor, ctrl->val); break; case V4L2_CID_SATURATION: ret = sc2235_set_ctrl_saturation(sensor, ctrl->val); break; case V4L2_CID_TEST_PATTERN: ret = sc2235_set_ctrl_test_pattern(sensor, ctrl->val); break; case V4L2_CID_POWER_LINE_FREQUENCY: ret = sc2235_set_ctrl_light_freq(sensor, ctrl->val); break; case V4L2_CID_HFLIP: ret = sc2235_set_ctrl_hflip(sensor, ctrl->val); break; case V4L2_CID_VFLIP: ret = sc2235_set_ctrl_vflip(sensor, ctrl->val); break; default: ret = -EINVAL; break; } return ret; } static const struct v4l2_ctrl_ops sc2235_ctrl_ops = { .g_volatile_ctrl = sc2235_g_volatile_ctrl, .s_ctrl = sc2235_s_ctrl, }; static int sc2235_init_controls(struct sc2235_dev *sensor) { const struct v4l2_ctrl_ops *ops = &sc2235_ctrl_ops; struct sc2235_ctrls *ctrls = &sensor->ctrls; struct v4l2_ctrl_handler *hdl = &ctrls->handler; int ret; v4l2_ctrl_handler_init(hdl, 32); /* we can use our own mutex for the ctrl lock */ hdl->lock = &sensor->lock; /* Clock related controls */ ctrls->pixel_rate = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_PIXEL_RATE, 0, INT_MAX, 1, sc2235_calc_pixel_rate(sensor)); /* Auto/manual white balance */ ctrls->auto_wb = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_AUTO_WHITE_BALANCE, 0, 1, 1, 1); ctrls->blue_balance = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_BLUE_BALANCE, 0, 4095, 1, 0); ctrls->red_balance = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_RED_BALANCE, 0, 4095, 1, 0); /* Auto/manual exposure */ ctrls->auto_exp = v4l2_ctrl_new_std_menu(hdl, ops, V4L2_CID_EXPOSURE_AUTO, V4L2_EXPOSURE_MANUAL, 0, V4L2_EXPOSURE_AUTO); ctrls->exposure = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_EXPOSURE, 0, 65535, 1, 0); /* Auto/manual gain */ ctrls->auto_gain = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_AUTOGAIN, 0, 1, 1, 1); ctrls->gain = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_GAIN, 0, 1023, 1, 0); ctrls->saturation = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_SATURATION, 0, 255, 1, 64); ctrls->hue = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_HUE, 0, 359, 1, 0); ctrls->contrast = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_CONTRAST, 0, 255, 1, 0); ctrls->test_pattern = v4l2_ctrl_new_std_menu_items(hdl, ops, V4L2_CID_TEST_PATTERN, ARRAY_SIZE(test_pattern_menu) - 1, 0, 0, test_pattern_menu); ctrls->hflip = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_HFLIP, 0, 1, 1, 0); ctrls->vflip = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_VFLIP, 0, 1, 1, 0); ctrls->light_freq = v4l2_ctrl_new_std_menu(hdl, ops, V4L2_CID_POWER_LINE_FREQUENCY, V4L2_CID_POWER_LINE_FREQUENCY_AUTO, 0, V4L2_CID_POWER_LINE_FREQUENCY_50HZ); if (hdl->error) { ret = hdl->error; goto free_ctrls; } ctrls->pixel_rate->flags |= V4L2_CTRL_FLAG_READ_ONLY; ctrls->gain->flags |= V4L2_CTRL_FLAG_VOLATILE; ctrls->exposure->flags |= V4L2_CTRL_FLAG_VOLATILE; v4l2_ctrl_auto_cluster(3, &ctrls->auto_wb, 0, false); v4l2_ctrl_auto_cluster(2, &ctrls->auto_gain, 0, true); v4l2_ctrl_auto_cluster(2, &ctrls->auto_exp, 1, true); sensor->sd.ctrl_handler = hdl; return 0; free_ctrls: v4l2_ctrl_handler_free(hdl); return ret; } static int sc2235_enum_frame_size(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_frame_size_enum *fse) { if (fse->pad != 0) return -EINVAL; if (fse->index >= SC2235_NUM_MODES) return -EINVAL; fse->min_width = sc2235_mode_data[fse->index].hact; fse->max_width = fse->min_width; fse->min_height = sc2235_mode_data[fse->index].vact; fse->max_height = fse->min_height; return 0; } static int sc2235_enum_frame_interval( struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_frame_interval_enum *fie) { struct sc2235_dev *sensor = to_sc2235_dev(sd); struct v4l2_fract tpf; int ret; if (fie->pad != 0) return -EINVAL; if (fie->index >= SC2235_NUM_FRAMERATES) return -EINVAL; tpf.numerator = 1; tpf.denominator = sc2235_framerates[fie->index]; ret = sc2235_try_frame_interval(sensor, &tpf, fie->width, fie->height); if (ret < 0) return -EINVAL; fie->interval = tpf; return 0; } static int sc2235_g_frame_interval(struct v4l2_subdev *sd, struct v4l2_subdev_frame_interval *fi) { struct sc2235_dev *sensor = to_sc2235_dev(sd); mutex_lock(&sensor->lock); fi->interval = sensor->frame_interval; mutex_unlock(&sensor->lock); return 0; } static int sc2235_s_frame_interval(struct v4l2_subdev *sd, struct v4l2_subdev_frame_interval *fi) { struct sc2235_dev *sensor = to_sc2235_dev(sd); const struct sc2235_mode_info *mode; int frame_rate, ret = 0; if (fi->pad != 0) return -EINVAL; mutex_lock(&sensor->lock); if (sensor->streaming) { ret = -EBUSY; goto out; } mode = sensor->current_mode; frame_rate = sc2235_try_frame_interval(sensor, &fi->interval, mode->hact, mode->vact); if (frame_rate < 0) { /* Always return a valid frame interval value */ fi->interval = sensor->frame_interval; goto out; } mode = sc2235_find_mode(sensor, frame_rate, mode->hact, mode->vact, true); if (!mode) { ret = -EINVAL; goto out; } if (mode != sensor->current_mode || frame_rate != sensor->current_fr) { sensor->current_fr = frame_rate; sensor->frame_interval = fi->interval; sensor->current_mode = mode; sensor->pending_mode_change = true; __v4l2_ctrl_s_ctrl_int64(sensor->ctrls.pixel_rate, sc2235_calc_pixel_rate(sensor)); } out: mutex_unlock(&sensor->lock); return ret; } static int sc2235_enum_mbus_code(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_mbus_code_enum *code) { if (code->pad != 0) return -EINVAL; if (code->index >= ARRAY_SIZE(sc2235_formats)) return -EINVAL; code->code = sc2235_formats[code->index].code; return 0; } static int sc2235_s_stream(struct v4l2_subdev *sd, int enable) { struct sc2235_dev *sensor = to_sc2235_dev(sd); int ret = 0; mutex_lock(&sensor->lock); if (sensor->streaming == !enable) { if (enable && sensor->pending_mode_change) { ret = sc2235_set_mode(sensor); if (ret) goto out; } if (enable && sensor->pending_fmt_change) { ret = sc2235_set_framefmt(sensor, &sensor->fmt); if (ret) goto out; sensor->pending_fmt_change = false; } if (sensor->ep.bus_type == V4L2_MBUS_PARALLEL) ret = sc2235_set_stream_dvp(sensor, enable); if (!ret) sensor->streaming = enable; } out: mutex_unlock(&sensor->lock); return ret; } static const struct v4l2_subdev_core_ops sc2235_core_ops = { .s_power = sc2235_s_power, .log_status = v4l2_ctrl_subdev_log_status, .subscribe_event = v4l2_ctrl_subdev_subscribe_event, .unsubscribe_event = v4l2_event_subdev_unsubscribe, }; static const struct v4l2_subdev_video_ops sc2235_video_ops = { .g_frame_interval = sc2235_g_frame_interval, .s_frame_interval = sc2235_s_frame_interval, .s_stream = sc2235_s_stream, }; static const struct v4l2_subdev_pad_ops sc2235_pad_ops = { .enum_mbus_code = sc2235_enum_mbus_code, .get_fmt = sc2235_get_fmt, .set_fmt = sc2235_set_fmt, .enum_frame_size = sc2235_enum_frame_size, .enum_frame_interval = sc2235_enum_frame_interval, }; static const struct v4l2_subdev_ops sc2235_subdev_ops = { .core = &sc2235_core_ops, .video = &sc2235_video_ops, .pad = &sc2235_pad_ops, }; static int sc2235_get_regulators(struct sc2235_dev *sensor) { int i; for (i = 0; i < SC2235_NUM_SUPPLIES; i++) sensor->supplies[i].supply = sc2235_supply_name[i]; return devm_regulator_bulk_get(&sensor->i2c_client->dev, SC2235_NUM_SUPPLIES, sensor->supplies); } static int sc2235_check_chip_id(struct sc2235_dev *sensor) { struct i2c_client *client = sensor->i2c_client; int ret = 0; u16 chip_id; ret = sc2235_set_power_on(sensor); if (ret) return ret; ret = sc2235_read_reg16(sensor, SC2235_REG_CHIP_ID, &chip_id); if (ret) { dev_err(&client->dev, "%s: failed to read chip identifier\n", __func__); goto power_off; } if (chip_id != SC2235_CHIP_ID) { dev_err(&client->dev, "%s: wrong chip identifier, expected 0x%x, got 0x%x\n", __func__, SC2235_CHIP_ID, chip_id); ret = -ENXIO; } dev_err(&client->dev, "%s: chip identifier, got 0x%x\n", __func__, chip_id); power_off: sc2235_set_power_off(sensor); return ret; } static int sc2235_probe(struct i2c_client *client) { struct device *dev = &client->dev; struct fwnode_handle *endpoint; struct sc2235_dev *sensor; struct v4l2_mbus_framefmt *fmt; u32 rotation; int ret; u8 chip_id_high, chip_id_low; sensor = devm_kzalloc(dev, sizeof(*sensor), GFP_KERNEL); if (!sensor) return -ENOMEM; sensor->i2c_client = client; dev_err(&client->dev, "probe %s: read chipid: 0x%x\n", __func__, (chip_id_high << 8) | chip_id_low); fmt = &sensor->fmt; fmt->code = MEDIA_BUS_FMT_SGBRG10_1X10; fmt->colorspace = V4L2_COLORSPACE_SRGB; fmt->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt->colorspace); fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE; fmt->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(fmt->colorspace); fmt->width = 1920; fmt->height = 1080; fmt->field = V4L2_FIELD_NONE; sensor->frame_interval.numerator = 1; sensor->frame_interval.denominator = sc2235_framerates[SC2235_30_FPS]; sensor->current_fr = SC2235_30_FPS; sensor->current_mode = &sc2235_mode_data[SC2235_MODE_1080P_1920_1080]; sensor->last_mode = sensor->current_mode; sensor->ae_target = 52; /* optional indication of physical rotation of sensor */ ret = fwnode_property_read_u32(dev_fwnode(&client->dev), "rotation", &rotation); if (!ret) { switch (rotation) { case 180: sensor->upside_down = true; fallthrough; case 0: break; default: dev_warn(dev, "%u degrees rotation is not supported, ignoring...\n", rotation); } } endpoint = fwnode_graph_get_next_endpoint(dev_fwnode(&client->dev), NULL); if (!endpoint) { dev_err(dev, "endpoint node not found\n"); return -EINVAL; } ret = v4l2_fwnode_endpoint_parse(endpoint, &sensor->ep); fwnode_handle_put(endpoint); if (ret) { dev_err(dev, "Could not parse endpoint\n"); return ret; } if (sensor->ep.bus_type != V4L2_MBUS_PARALLEL && sensor->ep.bus_type != V4L2_MBUS_CSI2_DPHY && sensor->ep.bus_type != V4L2_MBUS_BT656) { dev_err(dev, "Unsupported bus type %d\n", sensor->ep.bus_type); return -EINVAL; } /* get system clock (xclk) */ sensor->xclk = devm_clk_get(dev, "xclk"); if (IS_ERR(sensor->xclk)) { dev_err(dev, "failed to get xclk\n"); return PTR_ERR(sensor->xclk); } sensor->xclk_freq = clk_get_rate(sensor->xclk); if (sensor->xclk_freq < SC2235_XCLK_MIN || sensor->xclk_freq > SC2235_XCLK_MAX) { dev_err(dev, "xclk frequency out of range: %d Hz\n", sensor->xclk_freq); return -EINVAL; } /* request optional power down pin */ sensor->pwdn_gpio = devm_gpiod_get_optional(dev, "powerdown", GPIOD_OUT_HIGH); if (IS_ERR(sensor->pwdn_gpio)) return PTR_ERR(sensor->pwdn_gpio); /* request optional reset pin */ sensor->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); if (IS_ERR(sensor->reset_gpio)) return PTR_ERR(sensor->reset_gpio); v4l2_i2c_subdev_init(&sensor->sd, client, &sc2235_subdev_ops); sensor->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS; sensor->pad.flags = MEDIA_PAD_FL_SOURCE; sensor->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; ret = media_entity_pads_init(&sensor->sd.entity, 1, &sensor->pad); if (ret) return ret; ret = sc2235_get_regulators(sensor); if (ret) return ret; mutex_init(&sensor->lock); ret = sc2235_check_chip_id(sensor); if (ret) goto entity_cleanup; ret = sc2235_init_controls(sensor); if (ret) goto entity_cleanup; ret = v4l2_async_register_subdev_sensor(&sensor->sd); if (ret) goto free_ctrls; return 0; free_ctrls: v4l2_ctrl_handler_free(&sensor->ctrls.handler); entity_cleanup: media_entity_cleanup(&sensor->sd.entity); mutex_destroy(&sensor->lock); return ret; } static int sc2235_remove(struct i2c_client *client) { struct v4l2_subdev *sd = i2c_get_clientdata(client); struct sc2235_dev *sensor = to_sc2235_dev(sd); v4l2_async_unregister_subdev(&sensor->sd); media_entity_cleanup(&sensor->sd.entity); v4l2_ctrl_handler_free(&sensor->ctrls.handler); mutex_destroy(&sensor->lock); return 0; } static const struct i2c_device_id sc2235_id[] = { {"sc2235", 0}, {}, }; MODULE_DEVICE_TABLE(i2c, sc2235_id); static const struct of_device_id sc2235_dt_ids[] = { { .compatible = "sc2235" }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, sc2235_dt_ids); static struct i2c_driver sc2235_i2c_driver = { .driver = { .name = "sc2235", .of_match_table = sc2235_dt_ids, }, .id_table = sc2235_id, .probe_new = sc2235_probe, .remove = sc2235_remove, }; module_i2c_driver(sc2235_i2c_driver); MODULE_DESCRIPTION("SC2235 MIPI Camera Subdev Driver"); MODULE_LICENSE("GPL");