180a21da3SMarco Felsch // SPDX-License-Identifier: GPL-2.0-only 280a21da3SMarco Felsch /* 380a21da3SMarco Felsch * TC358746 - Parallel <-> CSI-2 Bridge 480a21da3SMarco Felsch * 580a21da3SMarco Felsch * Copyright 2022 Marco Felsch <kernel@pengutronix.de> 680a21da3SMarco Felsch * 780a21da3SMarco Felsch * Notes: 880a21da3SMarco Felsch * - Currently only 'Parallel-in -> CSI-out' mode is supported! 980a21da3SMarco Felsch */ 1080a21da3SMarco Felsch 1180a21da3SMarco Felsch #include <linux/bitfield.h> 1280a21da3SMarco Felsch #include <linux/clk.h> 1380a21da3SMarco Felsch #include <linux/clk-provider.h> 1480a21da3SMarco Felsch #include <linux/delay.h> 1580a21da3SMarco Felsch #include <linux/i2c.h> 1680a21da3SMarco Felsch #include <linux/interrupt.h> 1780a21da3SMarco Felsch #include <linux/kernel.h> 1880a21da3SMarco Felsch #include <linux/module.h> 1980a21da3SMarco Felsch #include <linux/phy/phy-mipi-dphy.h> 2080a21da3SMarco Felsch #include <linux/property.h> 2180a21da3SMarco Felsch #include <linux/pm_runtime.h> 2280a21da3SMarco Felsch #include <linux/regmap.h> 2380a21da3SMarco Felsch #include <linux/units.h> 2480a21da3SMarco Felsch #include <media/v4l2-ctrls.h> 2580a21da3SMarco Felsch #include <media/v4l2-device.h> 2680a21da3SMarco Felsch #include <media/v4l2-fwnode.h> 2780a21da3SMarco Felsch #include <media/v4l2-mc.h> 2880a21da3SMarco Felsch 2980a21da3SMarco Felsch /* 16-bit registers */ 3080a21da3SMarco Felsch #define CHIPID_REG 0x0000 3180a21da3SMarco Felsch #define CHIPID GENMASK(15, 8) 3280a21da3SMarco Felsch 3380a21da3SMarco Felsch #define SYSCTL_REG 0x0002 3480a21da3SMarco Felsch #define SRESET BIT(0) 3580a21da3SMarco Felsch 3680a21da3SMarco Felsch #define CONFCTL_REG 0x0004 3780a21da3SMarco Felsch #define PDATAF_MASK GENMASK(9, 8) 3880a21da3SMarco Felsch #define PDATAF_MODE0 0 3980a21da3SMarco Felsch #define PDATAF_MODE1 1 4080a21da3SMarco Felsch #define PDATAF_MODE2 2 4180a21da3SMarco Felsch #define PDATAF(val) FIELD_PREP(PDATAF_MASK, (val)) 4280a21da3SMarco Felsch #define PPEN BIT(6) 4380a21da3SMarco Felsch #define DATALANE_MASK GENMASK(1, 0) 4480a21da3SMarco Felsch 4580a21da3SMarco Felsch #define FIFOCTL_REG 0x0006 4680a21da3SMarco Felsch #define DATAFMT_REG 0x0008 4780a21da3SMarco Felsch #define PDFMT(val) FIELD_PREP(GENMASK(7, 4), (val)) 4880a21da3SMarco Felsch 4980a21da3SMarco Felsch #define MCLKCTL_REG 0x000c 5080a21da3SMarco Felsch #define MCLK_HIGH_MASK GENMASK(15, 8) 5180a21da3SMarco Felsch #define MCLK_LOW_MASK GENMASK(7, 0) 5280a21da3SMarco Felsch #define MCLK_HIGH(val) FIELD_PREP(MCLK_HIGH_MASK, (val)) 5380a21da3SMarco Felsch #define MCLK_LOW(val) FIELD_PREP(MCLK_LOW_MASK, (val)) 5480a21da3SMarco Felsch 5580a21da3SMarco Felsch #define PLLCTL0_REG 0x0016 5680a21da3SMarco Felsch #define PLL_PRD_MASK GENMASK(15, 12) 5780a21da3SMarco Felsch #define PLL_PRD(val) FIELD_PREP(PLL_PRD_MASK, (val)) 5880a21da3SMarco Felsch #define PLL_FBD_MASK GENMASK(8, 0) 5980a21da3SMarco Felsch #define PLL_FBD(val) FIELD_PREP(PLL_FBD_MASK, (val)) 6080a21da3SMarco Felsch 6180a21da3SMarco Felsch #define PLLCTL1_REG 0x0018 6280a21da3SMarco Felsch #define PLL_FRS_MASK GENMASK(11, 10) 6380a21da3SMarco Felsch #define PLL_FRS(val) FIELD_PREP(PLL_FRS_MASK, (val)) 6480a21da3SMarco Felsch #define CKEN BIT(4) 6580a21da3SMarco Felsch #define RESETB BIT(1) 6680a21da3SMarco Felsch #define PLL_EN BIT(0) 6780a21da3SMarco Felsch 6880a21da3SMarco Felsch #define CLKCTL_REG 0x0020 6980a21da3SMarco Felsch #define MCLKDIV_MASK GENMASK(3, 2) 7080a21da3SMarco Felsch #define MCLKDIV(val) FIELD_PREP(MCLKDIV_MASK, (val)) 7180a21da3SMarco Felsch #define MCLKDIV_8 0 7280a21da3SMarco Felsch #define MCLKDIV_4 1 7380a21da3SMarco Felsch #define MCLKDIV_2 2 7480a21da3SMarco Felsch 7580a21da3SMarco Felsch #define WORDCNT_REG 0x0022 7680a21da3SMarco Felsch #define PP_MISC_REG 0x0032 7780a21da3SMarco Felsch #define FRMSTOP BIT(15) 7880a21da3SMarco Felsch #define RSTPTR BIT(14) 7980a21da3SMarco Felsch 8080a21da3SMarco Felsch /* 32-bit registers */ 8180a21da3SMarco Felsch #define CLW_DPHYCONTTX_REG 0x0100 8280a21da3SMarco Felsch #define CLW_CNTRL_REG 0x0140 8380a21da3SMarco Felsch #define D0W_CNTRL_REG 0x0144 8480a21da3SMarco Felsch #define LANEDISABLE BIT(0) 8580a21da3SMarco Felsch 8680a21da3SMarco Felsch #define STARTCNTRL_REG 0x0204 8780a21da3SMarco Felsch #define START BIT(0) 8880a21da3SMarco Felsch 8980a21da3SMarco Felsch #define LINEINITCNT_REG 0x0210 9080a21da3SMarco Felsch #define LPTXTIMECNT_REG 0x0214 9180a21da3SMarco Felsch #define TCLK_HEADERCNT_REG 0x0218 9280a21da3SMarco Felsch #define TCLK_ZEROCNT(val) FIELD_PREP(GENMASK(15, 8), (val)) 9380a21da3SMarco Felsch #define TCLK_PREPARECNT(val) FIELD_PREP(GENMASK(6, 0), (val)) 9480a21da3SMarco Felsch 9580a21da3SMarco Felsch #define TCLK_TRAILCNT_REG 0x021C 9680a21da3SMarco Felsch #define THS_HEADERCNT_REG 0x0220 9780a21da3SMarco Felsch #define THS_ZEROCNT(val) FIELD_PREP(GENMASK(14, 8), (val)) 9880a21da3SMarco Felsch #define THS_PREPARECNT(val) FIELD_PREP(GENMASK(6, 0), (val)) 9980a21da3SMarco Felsch 10080a21da3SMarco Felsch #define TWAKEUP_REG 0x0224 10180a21da3SMarco Felsch #define TCLK_POSTCNT_REG 0x0228 10280a21da3SMarco Felsch #define THS_TRAILCNT_REG 0x022C 10380a21da3SMarco Felsch #define HSTXVREGEN_REG 0x0234 10480a21da3SMarco Felsch #define TXOPTIONCNTRL_REG 0x0238 10580a21da3SMarco Felsch #define CSI_CONTROL_REG 0x040C 10680a21da3SMarco Felsch #define CSI_MODE BIT(15) 10780a21da3SMarco Felsch #define TXHSMD BIT(7) 10880a21da3SMarco Felsch #define NOL(val) FIELD_PREP(GENMASK(2, 1), (val)) 10980a21da3SMarco Felsch 11080a21da3SMarco Felsch #define CSI_CONFW_REG 0x0500 11180a21da3SMarco Felsch #define MODE(val) FIELD_PREP(GENMASK(31, 29), (val)) 11280a21da3SMarco Felsch #define MODE_SET 0x5 11380a21da3SMarco Felsch #define ADDRESS(val) FIELD_PREP(GENMASK(28, 24), (val)) 11480a21da3SMarco Felsch #define CSI_CONTROL_ADDRESS 0x3 11580a21da3SMarco Felsch #define DATA(val) FIELD_PREP(GENMASK(15, 0), (val)) 11680a21da3SMarco Felsch 11780a21da3SMarco Felsch #define CSI_START_REG 0x0518 11880a21da3SMarco Felsch #define STRT BIT(0) 11980a21da3SMarco Felsch 12080a21da3SMarco Felsch static const struct v4l2_mbus_framefmt tc358746_def_fmt = { 12180a21da3SMarco Felsch .width = 640, 12280a21da3SMarco Felsch .height = 480, 12380a21da3SMarco Felsch .code = MEDIA_BUS_FMT_UYVY8_2X8, 12480a21da3SMarco Felsch .field = V4L2_FIELD_NONE, 12580a21da3SMarco Felsch .colorspace = V4L2_COLORSPACE_DEFAULT, 12680a21da3SMarco Felsch .ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT, 12780a21da3SMarco Felsch .quantization = V4L2_QUANTIZATION_DEFAULT, 12880a21da3SMarco Felsch .xfer_func = V4L2_XFER_FUNC_DEFAULT, 12980a21da3SMarco Felsch }; 13080a21da3SMarco Felsch 13180a21da3SMarco Felsch static const char * const tc358746_supplies[] = { 13280a21da3SMarco Felsch "vddc", "vddio", "vddmipi" 13380a21da3SMarco Felsch }; 13480a21da3SMarco Felsch 13580a21da3SMarco Felsch enum { 13680a21da3SMarco Felsch TC358746_SINK, 13780a21da3SMarco Felsch TC358746_SOURCE, 13880a21da3SMarco Felsch TC358746_NR_PADS 13980a21da3SMarco Felsch }; 14080a21da3SMarco Felsch 14180a21da3SMarco Felsch struct tc358746 { 14280a21da3SMarco Felsch struct v4l2_subdev sd; 14380a21da3SMarco Felsch struct media_pad pads[TC358746_NR_PADS]; 14480a21da3SMarco Felsch struct v4l2_async_notifier notifier; 14580a21da3SMarco Felsch struct v4l2_fwnode_endpoint csi_vep; 14680a21da3SMarco Felsch 14780a21da3SMarco Felsch struct v4l2_ctrl_handler ctrl_hdl; 14880a21da3SMarco Felsch 14980a21da3SMarco Felsch struct regmap *regmap; 15080a21da3SMarco Felsch struct clk *refclk; 15180a21da3SMarco Felsch struct gpio_desc *reset_gpio; 15280a21da3SMarco Felsch struct regulator_bulk_data supplies[ARRAY_SIZE(tc358746_supplies)]; 15380a21da3SMarco Felsch 15480a21da3SMarco Felsch struct clk_hw mclk_hw; 15580a21da3SMarco Felsch unsigned long mclk_rate; 15680a21da3SMarco Felsch u8 mclk_prediv; 15780a21da3SMarco Felsch u16 mclk_postdiv; 15880a21da3SMarco Felsch 15980a21da3SMarco Felsch unsigned long pll_rate; 16080a21da3SMarco Felsch u8 pll_post_div; 16180a21da3SMarco Felsch u16 pll_pre_div; 16280a21da3SMarco Felsch u16 pll_mul; 16380a21da3SMarco Felsch 16480a21da3SMarco Felsch #define TC358746_VB_MAX_SIZE (511 * 32) 16580a21da3SMarco Felsch #define TC358746_VB_DEFAULT_SIZE (1 * 32) 16680a21da3SMarco Felsch unsigned int vb_size; /* Video buffer size in bits */ 16780a21da3SMarco Felsch 16880a21da3SMarco Felsch struct phy_configure_opts_mipi_dphy dphy_cfg; 16980a21da3SMarco Felsch }; 17080a21da3SMarco Felsch 17180a21da3SMarco Felsch static inline struct tc358746 *to_tc358746(struct v4l2_subdev *sd) 17280a21da3SMarco Felsch { 17380a21da3SMarco Felsch return container_of(sd, struct tc358746, sd); 17480a21da3SMarco Felsch } 17580a21da3SMarco Felsch 17680a21da3SMarco Felsch static inline struct tc358746 *clk_hw_to_tc358746(struct clk_hw *hw) 17780a21da3SMarco Felsch { 17880a21da3SMarco Felsch return container_of(hw, struct tc358746, mclk_hw); 17980a21da3SMarco Felsch } 18080a21da3SMarco Felsch 18180a21da3SMarco Felsch struct tc358746_format { 18280a21da3SMarco Felsch u32 code; 18380a21da3SMarco Felsch bool csi_format; 18480a21da3SMarco Felsch unsigned char bus_width; 18580a21da3SMarco Felsch unsigned char bpp; 18680a21da3SMarco Felsch /* Register values */ 18780a21da3SMarco Felsch u8 pdformat; /* Peripheral Data Format */ 18880a21da3SMarco Felsch u8 pdataf; /* Parallel Data Format Option */ 18980a21da3SMarco Felsch }; 19080a21da3SMarco Felsch 19180a21da3SMarco Felsch enum { 19280a21da3SMarco Felsch PDFORMAT_RAW8 = 0, 19380a21da3SMarco Felsch PDFORMAT_RAW10, 19480a21da3SMarco Felsch PDFORMAT_RAW12, 19580a21da3SMarco Felsch PDFORMAT_RGB888, 19680a21da3SMarco Felsch PDFORMAT_RGB666, 19780a21da3SMarco Felsch PDFORMAT_RGB565, 19880a21da3SMarco Felsch PDFORMAT_YUV422_8BIT, 19980a21da3SMarco Felsch /* RESERVED = 7 */ 20080a21da3SMarco Felsch PDFORMAT_RAW14 = 8, 20180a21da3SMarco Felsch PDFORMAT_YUV422_10BIT, 20280a21da3SMarco Felsch PDFORMAT_YUV444, 20380a21da3SMarco Felsch }; 20480a21da3SMarco Felsch 20580a21da3SMarco Felsch /* Check tc358746_src_mbus_code() if you add new formats */ 20680a21da3SMarco Felsch static const struct tc358746_format tc358746_formats[] = { 20780a21da3SMarco Felsch { 20880a21da3SMarco Felsch .code = MEDIA_BUS_FMT_UYVY8_2X8, 20980a21da3SMarco Felsch .bus_width = 8, 21080a21da3SMarco Felsch .bpp = 16, 21180a21da3SMarco Felsch .pdformat = PDFORMAT_YUV422_8BIT, 21280a21da3SMarco Felsch .pdataf = PDATAF_MODE0, 21380a21da3SMarco Felsch }, { 21480a21da3SMarco Felsch .code = MEDIA_BUS_FMT_UYVY8_1X16, 21580a21da3SMarco Felsch .csi_format = true, 21680a21da3SMarco Felsch .bus_width = 16, 21780a21da3SMarco Felsch .bpp = 16, 21880a21da3SMarco Felsch .pdformat = PDFORMAT_YUV422_8BIT, 21980a21da3SMarco Felsch .pdataf = PDATAF_MODE1, 22080a21da3SMarco Felsch }, { 22180a21da3SMarco Felsch .code = MEDIA_BUS_FMT_YUYV8_1X16, 22280a21da3SMarco Felsch .csi_format = true, 22380a21da3SMarco Felsch .bus_width = 16, 22480a21da3SMarco Felsch .bpp = 16, 22580a21da3SMarco Felsch .pdformat = PDFORMAT_YUV422_8BIT, 22680a21da3SMarco Felsch .pdataf = PDATAF_MODE2, 22780a21da3SMarco Felsch }, { 22880a21da3SMarco Felsch .code = MEDIA_BUS_FMT_UYVY10_2X10, 22980a21da3SMarco Felsch .bus_width = 10, 23080a21da3SMarco Felsch .bpp = 20, 23180a21da3SMarco Felsch .pdformat = PDFORMAT_YUV422_10BIT, 23280a21da3SMarco Felsch .pdataf = PDATAF_MODE0, /* don't care */ 23380a21da3SMarco Felsch } 23480a21da3SMarco Felsch }; 23580a21da3SMarco Felsch 23680a21da3SMarco Felsch /* Get n-th format for pad */ 23780a21da3SMarco Felsch static const struct tc358746_format * 23880a21da3SMarco Felsch tc358746_get_format_by_idx(unsigned int pad, unsigned int index) 23980a21da3SMarco Felsch { 24080a21da3SMarco Felsch unsigned int idx = 0; 24180a21da3SMarco Felsch unsigned int i; 24280a21da3SMarco Felsch 24380a21da3SMarco Felsch for (i = 0; i < ARRAY_SIZE(tc358746_formats); i++) { 24480a21da3SMarco Felsch const struct tc358746_format *fmt = &tc358746_formats[i]; 24580a21da3SMarco Felsch 24680a21da3SMarco Felsch if ((pad == TC358746_SOURCE && fmt->csi_format) || 24780a21da3SMarco Felsch (pad == TC358746_SINK)) { 24880a21da3SMarco Felsch if (idx == index) 24980a21da3SMarco Felsch return fmt; 25080a21da3SMarco Felsch idx++; 25180a21da3SMarco Felsch } 25280a21da3SMarco Felsch } 25380a21da3SMarco Felsch 25480a21da3SMarco Felsch return ERR_PTR(-EINVAL); 25580a21da3SMarco Felsch } 25680a21da3SMarco Felsch 25780a21da3SMarco Felsch static const struct tc358746_format * 25880a21da3SMarco Felsch tc358746_get_format_by_code(unsigned int pad, u32 code) 25980a21da3SMarco Felsch { 26080a21da3SMarco Felsch unsigned int i; 26180a21da3SMarco Felsch 26280a21da3SMarco Felsch for (i = 0; i < ARRAY_SIZE(tc358746_formats); i++) { 26380a21da3SMarco Felsch const struct tc358746_format *fmt = &tc358746_formats[i]; 26480a21da3SMarco Felsch 26580a21da3SMarco Felsch if (pad == TC358746_SINK && fmt->code == code) 26680a21da3SMarco Felsch return fmt; 26780a21da3SMarco Felsch 26880a21da3SMarco Felsch if (pad == TC358746_SOURCE && !fmt->csi_format) 26980a21da3SMarco Felsch continue; 27080a21da3SMarco Felsch 27180a21da3SMarco Felsch if (fmt->code == code) 27280a21da3SMarco Felsch return fmt; 27380a21da3SMarco Felsch } 27480a21da3SMarco Felsch 27580a21da3SMarco Felsch return ERR_PTR(-EINVAL); 27680a21da3SMarco Felsch } 27780a21da3SMarco Felsch 27880a21da3SMarco Felsch static u32 tc358746_src_mbus_code(u32 code) 27980a21da3SMarco Felsch { 28080a21da3SMarco Felsch switch (code) { 28180a21da3SMarco Felsch case MEDIA_BUS_FMT_UYVY8_2X8: 28280a21da3SMarco Felsch return MEDIA_BUS_FMT_UYVY8_1X16; 28380a21da3SMarco Felsch case MEDIA_BUS_FMT_UYVY10_2X10: 28480a21da3SMarco Felsch return MEDIA_BUS_FMT_UYVY10_1X20; 28580a21da3SMarco Felsch default: 28680a21da3SMarco Felsch return code; 28780a21da3SMarco Felsch } 28880a21da3SMarco Felsch } 28980a21da3SMarco Felsch 29080a21da3SMarco Felsch static bool tc358746_valid_reg(struct device *dev, unsigned int reg) 29180a21da3SMarco Felsch { 29280a21da3SMarco Felsch switch (reg) { 29380a21da3SMarco Felsch case CHIPID_REG ... CSI_START_REG: 29480a21da3SMarco Felsch return true; 29580a21da3SMarco Felsch default: 29680a21da3SMarco Felsch return false; 29780a21da3SMarco Felsch } 29880a21da3SMarco Felsch } 29980a21da3SMarco Felsch 30080a21da3SMarco Felsch static const struct regmap_config tc358746_regmap_config = { 30180a21da3SMarco Felsch .name = "tc358746", 30280a21da3SMarco Felsch .reg_bits = 16, 30380a21da3SMarco Felsch .val_bits = 16, 30480a21da3SMarco Felsch .max_register = CSI_START_REG, 30580a21da3SMarco Felsch .writeable_reg = tc358746_valid_reg, 30680a21da3SMarco Felsch .readable_reg = tc358746_valid_reg, 30780a21da3SMarco Felsch .reg_format_endian = REGMAP_ENDIAN_BIG, 30880a21da3SMarco Felsch .val_format_endian = REGMAP_ENDIAN_BIG, 30980a21da3SMarco Felsch }; 31080a21da3SMarco Felsch 31180a21da3SMarco Felsch static int tc358746_write(struct tc358746 *tc358746, u32 reg, u32 val) 31280a21da3SMarco Felsch { 31380a21da3SMarco Felsch size_t count; 31480a21da3SMarco Felsch int err; 31580a21da3SMarco Felsch 31680a21da3SMarco Felsch /* 32-bit registers starting from CLW_DPHYCONTTX */ 31780a21da3SMarco Felsch count = reg < CLW_DPHYCONTTX_REG ? 1 : 2; 31880a21da3SMarco Felsch 31980a21da3SMarco Felsch err = regmap_bulk_write(tc358746->regmap, reg, &val, count); 32080a21da3SMarco Felsch if (err) 32180a21da3SMarco Felsch dev_err(tc358746->sd.dev, 32280a21da3SMarco Felsch "Failed to write reg:0x%04x err:%d\n", reg, err); 32380a21da3SMarco Felsch 32480a21da3SMarco Felsch return err; 32580a21da3SMarco Felsch } 32680a21da3SMarco Felsch 32780a21da3SMarco Felsch static int tc358746_read(struct tc358746 *tc358746, u32 reg, u32 *val) 32880a21da3SMarco Felsch { 32980a21da3SMarco Felsch size_t count; 33080a21da3SMarco Felsch int err; 33180a21da3SMarco Felsch 33280a21da3SMarco Felsch /* 32-bit registers starting from CLW_DPHYCONTTX */ 33380a21da3SMarco Felsch count = reg < CLW_DPHYCONTTX_REG ? 1 : 2; 33480a21da3SMarco Felsch *val = 0; 33580a21da3SMarco Felsch 33680a21da3SMarco Felsch err = regmap_bulk_read(tc358746->regmap, reg, val, count); 33780a21da3SMarco Felsch if (err) 33880a21da3SMarco Felsch dev_err(tc358746->sd.dev, 33980a21da3SMarco Felsch "Failed to read reg:0x%04x err:%d\n", reg, err); 34080a21da3SMarco Felsch 34180a21da3SMarco Felsch return err; 34280a21da3SMarco Felsch } 34380a21da3SMarco Felsch 34480a21da3SMarco Felsch static int 34580a21da3SMarco Felsch tc358746_update_bits(struct tc358746 *tc358746, u32 reg, u32 mask, u32 val) 34680a21da3SMarco Felsch { 34780a21da3SMarco Felsch u32 tmp, orig; 34880a21da3SMarco Felsch int err; 34980a21da3SMarco Felsch 35080a21da3SMarco Felsch err = tc358746_read(tc358746, reg, &orig); 35180a21da3SMarco Felsch if (err) 35280a21da3SMarco Felsch return err; 35380a21da3SMarco Felsch 35480a21da3SMarco Felsch tmp = orig & ~mask; 35580a21da3SMarco Felsch tmp |= val & mask; 35680a21da3SMarco Felsch 35780a21da3SMarco Felsch return tc358746_write(tc358746, reg, tmp); 35880a21da3SMarco Felsch } 35980a21da3SMarco Felsch 36080a21da3SMarco Felsch static int tc358746_set_bits(struct tc358746 *tc358746, u32 reg, u32 bits) 36180a21da3SMarco Felsch { 36280a21da3SMarco Felsch return tc358746_update_bits(tc358746, reg, bits, bits); 36380a21da3SMarco Felsch } 36480a21da3SMarco Felsch 36580a21da3SMarco Felsch static int tc358746_clear_bits(struct tc358746 *tc358746, u32 reg, u32 bits) 36680a21da3SMarco Felsch { 36780a21da3SMarco Felsch return tc358746_update_bits(tc358746, reg, bits, 0); 36880a21da3SMarco Felsch } 36980a21da3SMarco Felsch 37080a21da3SMarco Felsch static int tc358746_sw_reset(struct tc358746 *tc358746) 37180a21da3SMarco Felsch { 37280a21da3SMarco Felsch int err; 37380a21da3SMarco Felsch 37480a21da3SMarco Felsch err = tc358746_set_bits(tc358746, SYSCTL_REG, SRESET); 37580a21da3SMarco Felsch if (err) 37680a21da3SMarco Felsch return err; 37780a21da3SMarco Felsch 37880a21da3SMarco Felsch fsleep(10); 37980a21da3SMarco Felsch 38080a21da3SMarco Felsch return tc358746_clear_bits(tc358746, SYSCTL_REG, SRESET); 38180a21da3SMarco Felsch } 38280a21da3SMarco Felsch 38380a21da3SMarco Felsch static int 38480a21da3SMarco Felsch tc358746_apply_pll_config(struct tc358746 *tc358746) 38580a21da3SMarco Felsch { 38680a21da3SMarco Felsch u8 post = tc358746->pll_post_div; 38780a21da3SMarco Felsch u16 pre = tc358746->pll_pre_div; 38880a21da3SMarco Felsch u16 mul = tc358746->pll_mul; 38980a21da3SMarco Felsch u32 val, mask; 39080a21da3SMarco Felsch int err; 39180a21da3SMarco Felsch 39280a21da3SMarco Felsch err = tc358746_read(tc358746, PLLCTL1_REG, &val); 39380a21da3SMarco Felsch if (err) 39480a21da3SMarco Felsch return err; 39580a21da3SMarco Felsch 39680a21da3SMarco Felsch /* Don't touch the PLL if running */ 39780a21da3SMarco Felsch if (FIELD_GET(PLL_EN, val) == 1) 39880a21da3SMarco Felsch return 0; 39980a21da3SMarco Felsch 40080a21da3SMarco Felsch /* Pre-div and Multiplicator have a internal +1 logic */ 40180a21da3SMarco Felsch val = PLL_PRD(pre - 1) | PLL_FBD(mul - 1); 40280a21da3SMarco Felsch mask = PLL_PRD_MASK | PLL_FBD_MASK; 40380a21da3SMarco Felsch err = tc358746_update_bits(tc358746, PLLCTL0_REG, mask, val); 40480a21da3SMarco Felsch if (err) 40580a21da3SMarco Felsch return err; 40680a21da3SMarco Felsch 40780a21da3SMarco Felsch val = PLL_FRS(ilog2(post)) | RESETB | PLL_EN; 40880a21da3SMarco Felsch mask = PLL_FRS_MASK | RESETB | PLL_EN; 40906050811SMarco Felsch err = tc358746_update_bits(tc358746, PLLCTL1_REG, mask, val); 41080a21da3SMarco Felsch if (err) 41180a21da3SMarco Felsch return err; 41280a21da3SMarco Felsch 41380a21da3SMarco Felsch fsleep(1000); 41480a21da3SMarco Felsch 41580a21da3SMarco Felsch return tc358746_set_bits(tc358746, PLLCTL1_REG, CKEN); 41680a21da3SMarco Felsch } 41780a21da3SMarco Felsch 41880a21da3SMarco Felsch static int tc358746_apply_misc_config(struct tc358746 *tc358746) 41980a21da3SMarco Felsch { 42080a21da3SMarco Felsch const struct v4l2_mbus_framefmt *mbusfmt; 42180a21da3SMarco Felsch struct v4l2_subdev *sd = &tc358746->sd; 42280a21da3SMarco Felsch struct v4l2_subdev_state *sink_state; 42380a21da3SMarco Felsch const struct tc358746_format *fmt; 42480a21da3SMarco Felsch struct device *dev = sd->dev; 42580a21da3SMarco Felsch u32 val; 42680a21da3SMarco Felsch int err; 42780a21da3SMarco Felsch 42880a21da3SMarco Felsch sink_state = v4l2_subdev_lock_and_get_active_state(sd); 42980a21da3SMarco Felsch 43080a21da3SMarco Felsch mbusfmt = v4l2_subdev_get_pad_format(sd, sink_state, TC358746_SINK); 43180a21da3SMarco Felsch fmt = tc358746_get_format_by_code(TC358746_SINK, mbusfmt->code); 43280a21da3SMarco Felsch 43380a21da3SMarco Felsch /* Self defined CSI user data type id's are not supported yet */ 43480a21da3SMarco Felsch val = PDFMT(fmt->pdformat); 43580a21da3SMarco Felsch dev_dbg(dev, "DATAFMT: 0x%x\n", val); 43680a21da3SMarco Felsch err = tc358746_write(tc358746, DATAFMT_REG, val); 43780a21da3SMarco Felsch if (err) 43880a21da3SMarco Felsch goto out; 43980a21da3SMarco Felsch 44080a21da3SMarco Felsch val = PDATAF(fmt->pdataf); 44180a21da3SMarco Felsch dev_dbg(dev, "CONFCTL[PDATAF]: 0x%x\n", fmt->pdataf); 44280a21da3SMarco Felsch err = tc358746_update_bits(tc358746, CONFCTL_REG, PDATAF_MASK, val); 44380a21da3SMarco Felsch if (err) 44480a21da3SMarco Felsch goto out; 44580a21da3SMarco Felsch 44680a21da3SMarco Felsch val = tc358746->vb_size / 32; 44780a21da3SMarco Felsch dev_dbg(dev, "FIFOCTL: %u (0x%x)\n", val, val); 44880a21da3SMarco Felsch err = tc358746_write(tc358746, FIFOCTL_REG, val); 44980a21da3SMarco Felsch if (err) 45080a21da3SMarco Felsch goto out; 45180a21da3SMarco Felsch 45280a21da3SMarco Felsch /* Total number of bytes for each line/width */ 45380a21da3SMarco Felsch val = mbusfmt->width * fmt->bpp / 8; 45480a21da3SMarco Felsch dev_dbg(dev, "WORDCNT: %u (0x%x)\n", val, val); 45580a21da3SMarco Felsch err = tc358746_write(tc358746, WORDCNT_REG, val); 45680a21da3SMarco Felsch 45780a21da3SMarco Felsch out: 45880a21da3SMarco Felsch v4l2_subdev_unlock_state(sink_state); 45980a21da3SMarco Felsch 46080a21da3SMarco Felsch return err; 46180a21da3SMarco Felsch } 46280a21da3SMarco Felsch 46380a21da3SMarco Felsch /* Use MHz as base so the div needs no u64 */ 46480a21da3SMarco Felsch static u32 tc358746_cfg_to_cnt(unsigned int cfg_val, 46580a21da3SMarco Felsch unsigned int clk_mhz, 46680a21da3SMarco Felsch unsigned int time_base) 46780a21da3SMarco Felsch { 46880a21da3SMarco Felsch return DIV_ROUND_UP(cfg_val * clk_mhz, time_base); 46980a21da3SMarco Felsch } 47080a21da3SMarco Felsch 47180a21da3SMarco Felsch static u32 tc358746_ps_to_cnt(unsigned int cfg_val, 47280a21da3SMarco Felsch unsigned int clk_mhz) 47380a21da3SMarco Felsch { 47480a21da3SMarco Felsch return tc358746_cfg_to_cnt(cfg_val, clk_mhz, USEC_PER_SEC); 47580a21da3SMarco Felsch } 47680a21da3SMarco Felsch 47780a21da3SMarco Felsch static u32 tc358746_us_to_cnt(unsigned int cfg_val, 47880a21da3SMarco Felsch unsigned int clk_mhz) 47980a21da3SMarco Felsch { 48080a21da3SMarco Felsch return tc358746_cfg_to_cnt(cfg_val, clk_mhz, 1); 48180a21da3SMarco Felsch } 48280a21da3SMarco Felsch 48380a21da3SMarco Felsch static int tc358746_apply_dphy_config(struct tc358746 *tc358746) 48480a21da3SMarco Felsch { 48580a21da3SMarco Felsch struct phy_configure_opts_mipi_dphy *cfg = &tc358746->dphy_cfg; 48680a21da3SMarco Felsch bool non_cont_clk = !!(tc358746->csi_vep.bus.mipi_csi2.flags & 48780a21da3SMarco Felsch V4L2_MBUS_CSI2_NONCONTINUOUS_CLOCK); 48880a21da3SMarco Felsch struct device *dev = tc358746->sd.dev; 48980a21da3SMarco Felsch unsigned long hs_byte_clk, hf_clk; 49080a21da3SMarco Felsch u32 val, val2, lptxcnt; 49180a21da3SMarco Felsch int err; 49280a21da3SMarco Felsch 49380a21da3SMarco Felsch /* The hs_byte_clk is also called SYSCLK in the excel sheet */ 49480a21da3SMarco Felsch hs_byte_clk = cfg->hs_clk_rate / 8; 49580a21da3SMarco Felsch hs_byte_clk /= HZ_PER_MHZ; 49680a21da3SMarco Felsch hf_clk = hs_byte_clk / 2; 49780a21da3SMarco Felsch 49880a21da3SMarco Felsch val = tc358746_us_to_cnt(cfg->init, hf_clk) - 1; 49980a21da3SMarco Felsch dev_dbg(dev, "LINEINITCNT: %u (0x%x)\n", val, val); 50080a21da3SMarco Felsch err = tc358746_write(tc358746, LINEINITCNT_REG, val); 50180a21da3SMarco Felsch if (err) 50280a21da3SMarco Felsch return err; 50380a21da3SMarco Felsch 50480a21da3SMarco Felsch val = tc358746_ps_to_cnt(cfg->lpx, hs_byte_clk) - 1; 50580a21da3SMarco Felsch lptxcnt = val; 50680a21da3SMarco Felsch dev_dbg(dev, "LPTXTIMECNT: %u (0x%x)\n", val, val); 50780a21da3SMarco Felsch err = tc358746_write(tc358746, LPTXTIMECNT_REG, val); 50880a21da3SMarco Felsch if (err) 50980a21da3SMarco Felsch return err; 51080a21da3SMarco Felsch 51180a21da3SMarco Felsch val = tc358746_ps_to_cnt(cfg->clk_prepare, hs_byte_clk) - 1; 51280a21da3SMarco Felsch val2 = tc358746_ps_to_cnt(cfg->clk_zero, hs_byte_clk) - 1; 51380a21da3SMarco Felsch dev_dbg(dev, "TCLK_PREPARECNT: %u (0x%x)\n", val, val); 51480a21da3SMarco Felsch dev_dbg(dev, "TCLK_ZEROCNT: %u (0x%x)\n", val2, val2); 51580a21da3SMarco Felsch dev_dbg(dev, "TCLK_HEADERCNT: 0x%x\n", 51680a21da3SMarco Felsch (u32)(TCLK_PREPARECNT(val) | TCLK_ZEROCNT(val2))); 51780a21da3SMarco Felsch err = tc358746_write(tc358746, TCLK_HEADERCNT_REG, 51880a21da3SMarco Felsch TCLK_PREPARECNT(val) | TCLK_ZEROCNT(val2)); 51980a21da3SMarco Felsch if (err) 52080a21da3SMarco Felsch return err; 52180a21da3SMarco Felsch 52280a21da3SMarco Felsch val = tc358746_ps_to_cnt(cfg->clk_trail, hs_byte_clk); 52380a21da3SMarco Felsch dev_dbg(dev, "TCLK_TRAILCNT: %u (0x%x)\n", val, val); 52480a21da3SMarco Felsch err = tc358746_write(tc358746, TCLK_TRAILCNT_REG, val); 52580a21da3SMarco Felsch if (err) 52680a21da3SMarco Felsch return err; 52780a21da3SMarco Felsch 52880a21da3SMarco Felsch val = tc358746_ps_to_cnt(cfg->hs_prepare, hs_byte_clk) - 1; 52980a21da3SMarco Felsch val2 = tc358746_ps_to_cnt(cfg->hs_zero, hs_byte_clk) - 1; 53080a21da3SMarco Felsch dev_dbg(dev, "THS_PREPARECNT: %u (0x%x)\n", val, val); 53180a21da3SMarco Felsch dev_dbg(dev, "THS_ZEROCNT: %u (0x%x)\n", val2, val2); 53280a21da3SMarco Felsch dev_dbg(dev, "THS_HEADERCNT: 0x%x\n", 53380a21da3SMarco Felsch (u32)(THS_PREPARECNT(val) | THS_ZEROCNT(val2))); 53480a21da3SMarco Felsch err = tc358746_write(tc358746, THS_HEADERCNT_REG, 53580a21da3SMarco Felsch THS_PREPARECNT(val) | THS_ZEROCNT(val2)); 53680a21da3SMarco Felsch if (err) 53780a21da3SMarco Felsch return err; 53880a21da3SMarco Felsch 53980a21da3SMarco Felsch /* TWAKEUP > 1ms in lptxcnt steps */ 54080a21da3SMarco Felsch val = tc358746_us_to_cnt(cfg->wakeup, hs_byte_clk); 54180a21da3SMarco Felsch val = val / (lptxcnt + 1) - 1; 54280a21da3SMarco Felsch dev_dbg(dev, "TWAKEUP: %u (0x%x)\n", val, val); 54380a21da3SMarco Felsch err = tc358746_write(tc358746, TWAKEUP_REG, val); 54480a21da3SMarco Felsch if (err) 54580a21da3SMarco Felsch return err; 54680a21da3SMarco Felsch 54780a21da3SMarco Felsch val = tc358746_ps_to_cnt(cfg->clk_post, hs_byte_clk); 54880a21da3SMarco Felsch dev_dbg(dev, "TCLK_POSTCNT: %u (0x%x)\n", val, val); 54980a21da3SMarco Felsch err = tc358746_write(tc358746, TCLK_POSTCNT_REG, val); 55080a21da3SMarco Felsch if (err) 55180a21da3SMarco Felsch return err; 55280a21da3SMarco Felsch 55380a21da3SMarco Felsch val = tc358746_ps_to_cnt(cfg->hs_trail, hs_byte_clk); 55480a21da3SMarco Felsch dev_dbg(dev, "THS_TRAILCNT: %u (0x%x)\n", val, val); 55580a21da3SMarco Felsch err = tc358746_write(tc358746, THS_TRAILCNT_REG, val); 55680a21da3SMarco Felsch if (err) 55780a21da3SMarco Felsch return err; 55880a21da3SMarco Felsch 55980a21da3SMarco Felsch dev_dbg(dev, "CONTCLKMODE: %u", non_cont_clk ? 0 : 1); 56080a21da3SMarco Felsch 56180a21da3SMarco Felsch return tc358746_write(tc358746, TXOPTIONCNTRL_REG, non_cont_clk ? 0 : 1); 56280a21da3SMarco Felsch } 56380a21da3SMarco Felsch 56480a21da3SMarco Felsch #define MAX_DATA_LANES 4 56580a21da3SMarco Felsch 56680a21da3SMarco Felsch static int tc358746_enable_csi_lanes(struct tc358746 *tc358746, int enable) 56780a21da3SMarco Felsch { 56880a21da3SMarco Felsch unsigned int lanes = tc358746->dphy_cfg.lanes; 56980a21da3SMarco Felsch unsigned int lane; 57080a21da3SMarco Felsch u32 reg, val; 57180a21da3SMarco Felsch int err; 57280a21da3SMarco Felsch 57380a21da3SMarco Felsch err = tc358746_update_bits(tc358746, CONFCTL_REG, DATALANE_MASK, 57480a21da3SMarco Felsch lanes - 1); 57580a21da3SMarco Felsch if (err) 57680a21da3SMarco Felsch return err; 57780a21da3SMarco Felsch 57880a21da3SMarco Felsch /* Clock lane */ 57980a21da3SMarco Felsch val = enable ? 0 : LANEDISABLE; 58080a21da3SMarco Felsch dev_dbg(tc358746->sd.dev, "CLW_CNTRL: 0x%x\n", val); 58180a21da3SMarco Felsch err = tc358746_write(tc358746, CLW_CNTRL_REG, val); 58280a21da3SMarco Felsch if (err) 58380a21da3SMarco Felsch return err; 58480a21da3SMarco Felsch 58580a21da3SMarco Felsch for (lane = 0; lane < MAX_DATA_LANES; lane++) { 58680a21da3SMarco Felsch /* Data lanes */ 58780a21da3SMarco Felsch reg = D0W_CNTRL_REG + lane * 0x4; 58880a21da3SMarco Felsch val = (enable && lane < lanes) ? 0 : LANEDISABLE; 58980a21da3SMarco Felsch 59080a21da3SMarco Felsch dev_dbg(tc358746->sd.dev, "D%uW_CNTRL: 0x%x\n", lane, val); 59180a21da3SMarco Felsch err = tc358746_write(tc358746, reg, val); 59280a21da3SMarco Felsch if (err) 59380a21da3SMarco Felsch return err; 59480a21da3SMarco Felsch } 59580a21da3SMarco Felsch 59680a21da3SMarco Felsch val = 0; 59780a21da3SMarco Felsch if (enable) { 59880a21da3SMarco Felsch /* Clock lane */ 59980a21da3SMarco Felsch val |= BIT(0); 60080a21da3SMarco Felsch 60180a21da3SMarco Felsch /* Data lanes */ 60280a21da3SMarco Felsch for (lane = 1; lane <= lanes; lane++) 60380a21da3SMarco Felsch val |= BIT(lane); 60480a21da3SMarco Felsch } 60580a21da3SMarco Felsch 60680a21da3SMarco Felsch dev_dbg(tc358746->sd.dev, "HSTXVREGEN: 0x%x\n", val); 60780a21da3SMarco Felsch 60880a21da3SMarco Felsch return tc358746_write(tc358746, HSTXVREGEN_REG, val); 60980a21da3SMarco Felsch } 61080a21da3SMarco Felsch 61180a21da3SMarco Felsch static int tc358746_enable_csi_module(struct tc358746 *tc358746, int enable) 61280a21da3SMarco Felsch { 61380a21da3SMarco Felsch unsigned int lanes = tc358746->dphy_cfg.lanes; 61480a21da3SMarco Felsch int err; 61580a21da3SMarco Felsch 61680a21da3SMarco Felsch /* 61780a21da3SMarco Felsch * START and STRT are only reseted/disabled by sw reset. This is 61880a21da3SMarco Felsch * required to put the lane state back into LP-11 state. The sw reset 61980a21da3SMarco Felsch * don't reset register values. 62080a21da3SMarco Felsch */ 62180a21da3SMarco Felsch if (!enable) 62280a21da3SMarco Felsch return tc358746_sw_reset(tc358746); 62380a21da3SMarco Felsch 62480a21da3SMarco Felsch err = tc358746_write(tc358746, STARTCNTRL_REG, START); 62580a21da3SMarco Felsch if (err) 62680a21da3SMarco Felsch return err; 62780a21da3SMarco Felsch 62880a21da3SMarco Felsch err = tc358746_write(tc358746, CSI_START_REG, STRT); 62980a21da3SMarco Felsch if (err) 63080a21da3SMarco Felsch return err; 63180a21da3SMarco Felsch 63280a21da3SMarco Felsch /* CSI_CONTROL_REG is only indirect accessible */ 63380a21da3SMarco Felsch return tc358746_write(tc358746, CSI_CONFW_REG, 63480a21da3SMarco Felsch MODE(MODE_SET) | 63580a21da3SMarco Felsch ADDRESS(CSI_CONTROL_ADDRESS) | 63680a21da3SMarco Felsch DATA(CSI_MODE | TXHSMD | NOL(lanes - 1))); 63780a21da3SMarco Felsch } 63880a21da3SMarco Felsch 63980a21da3SMarco Felsch static int tc358746_enable_parallel_port(struct tc358746 *tc358746, int enable) 64080a21da3SMarco Felsch { 64180a21da3SMarco Felsch int err; 64280a21da3SMarco Felsch 64380a21da3SMarco Felsch if (enable) { 64480a21da3SMarco Felsch err = tc358746_write(tc358746, PP_MISC_REG, 0); 64580a21da3SMarco Felsch if (err) 64680a21da3SMarco Felsch return err; 64780a21da3SMarco Felsch 64880a21da3SMarco Felsch return tc358746_set_bits(tc358746, CONFCTL_REG, PPEN); 64980a21da3SMarco Felsch } 65080a21da3SMarco Felsch 65180a21da3SMarco Felsch err = tc358746_set_bits(tc358746, PP_MISC_REG, FRMSTOP); 65280a21da3SMarco Felsch if (err) 65380a21da3SMarco Felsch return err; 65480a21da3SMarco Felsch 65580a21da3SMarco Felsch err = tc358746_clear_bits(tc358746, CONFCTL_REG, PPEN); 65680a21da3SMarco Felsch if (err) 65780a21da3SMarco Felsch return err; 65880a21da3SMarco Felsch 65980a21da3SMarco Felsch return tc358746_set_bits(tc358746, PP_MISC_REG, RSTPTR); 66080a21da3SMarco Felsch } 66180a21da3SMarco Felsch 66280a21da3SMarco Felsch static inline struct v4l2_subdev *tc358746_get_remote_sd(struct media_pad *pad) 66380a21da3SMarco Felsch { 66480a21da3SMarco Felsch pad = media_pad_remote_pad_first(pad); 66580a21da3SMarco Felsch if (!pad) 66680a21da3SMarco Felsch return NULL; 66780a21da3SMarco Felsch 66880a21da3SMarco Felsch return media_entity_to_v4l2_subdev(pad->entity); 66980a21da3SMarco Felsch } 67080a21da3SMarco Felsch 67180a21da3SMarco Felsch static int tc358746_s_stream(struct v4l2_subdev *sd, int enable) 67280a21da3SMarco Felsch { 67380a21da3SMarco Felsch struct tc358746 *tc358746 = to_tc358746(sd); 67480a21da3SMarco Felsch struct v4l2_subdev *src; 67580a21da3SMarco Felsch int err; 67680a21da3SMarco Felsch 67780a21da3SMarco Felsch dev_dbg(sd->dev, "%sable\n", enable ? "en" : "dis"); 67880a21da3SMarco Felsch 67980a21da3SMarco Felsch src = tc358746_get_remote_sd(&tc358746->pads[TC358746_SINK]); 68080a21da3SMarco Felsch if (!src) 68180a21da3SMarco Felsch return -EPIPE; 68280a21da3SMarco Felsch 68380a21da3SMarco Felsch if (enable) { 68480a21da3SMarco Felsch err = pm_runtime_resume_and_get(sd->dev); 68580a21da3SMarco Felsch if (err) 68680a21da3SMarco Felsch return err; 68780a21da3SMarco Felsch 68880a21da3SMarco Felsch err = tc358746_apply_dphy_config(tc358746); 68980a21da3SMarco Felsch if (err) 69080a21da3SMarco Felsch goto err_out; 69180a21da3SMarco Felsch 69280a21da3SMarco Felsch err = tc358746_apply_misc_config(tc358746); 69380a21da3SMarco Felsch if (err) 69480a21da3SMarco Felsch goto err_out; 69580a21da3SMarco Felsch 69680a21da3SMarco Felsch err = tc358746_enable_csi_lanes(tc358746, 1); 69780a21da3SMarco Felsch if (err) 69880a21da3SMarco Felsch goto err_out; 69980a21da3SMarco Felsch 70080a21da3SMarco Felsch err = tc358746_enable_csi_module(tc358746, 1); 70180a21da3SMarco Felsch if (err) 70280a21da3SMarco Felsch goto err_out; 70380a21da3SMarco Felsch 70480a21da3SMarco Felsch err = tc358746_enable_parallel_port(tc358746, 1); 70580a21da3SMarco Felsch if (err) 70680a21da3SMarco Felsch goto err_out; 70780a21da3SMarco Felsch 70880a21da3SMarco Felsch err = v4l2_subdev_call(src, video, s_stream, 1); 70980a21da3SMarco Felsch if (err) 71080a21da3SMarco Felsch goto err_out; 71180a21da3SMarco Felsch 71280a21da3SMarco Felsch return 0; 71380a21da3SMarco Felsch 71480a21da3SMarco Felsch err_out: 71580a21da3SMarco Felsch pm_runtime_mark_last_busy(sd->dev); 71680a21da3SMarco Felsch pm_runtime_put_sync_autosuspend(sd->dev); 71780a21da3SMarco Felsch 71880a21da3SMarco Felsch return err; 71980a21da3SMarco Felsch } 72080a21da3SMarco Felsch 72180a21da3SMarco Felsch /* 72280a21da3SMarco Felsch * The lanes must be disabled first (before the csi module) so the 72380a21da3SMarco Felsch * LP-11 state is entered correctly. 72480a21da3SMarco Felsch */ 72580a21da3SMarco Felsch err = tc358746_enable_csi_lanes(tc358746, 0); 72680a21da3SMarco Felsch if (err) 72780a21da3SMarco Felsch return err; 72880a21da3SMarco Felsch 72980a21da3SMarco Felsch err = tc358746_enable_csi_module(tc358746, 0); 73080a21da3SMarco Felsch if (err) 73180a21da3SMarco Felsch return err; 73280a21da3SMarco Felsch 73380a21da3SMarco Felsch err = tc358746_enable_parallel_port(tc358746, 0); 73480a21da3SMarco Felsch if (err) 73580a21da3SMarco Felsch return err; 73680a21da3SMarco Felsch 73780a21da3SMarco Felsch pm_runtime_mark_last_busy(sd->dev); 73880a21da3SMarco Felsch pm_runtime_put_sync_autosuspend(sd->dev); 73980a21da3SMarco Felsch 74080a21da3SMarco Felsch return v4l2_subdev_call(src, video, s_stream, 0); 74180a21da3SMarco Felsch } 74280a21da3SMarco Felsch 74380a21da3SMarco Felsch static int tc358746_init_cfg(struct v4l2_subdev *sd, 74480a21da3SMarco Felsch struct v4l2_subdev_state *state) 74580a21da3SMarco Felsch { 74680a21da3SMarco Felsch struct v4l2_mbus_framefmt *fmt; 74780a21da3SMarco Felsch 74880a21da3SMarco Felsch fmt = v4l2_subdev_get_pad_format(sd, state, TC358746_SINK); 74980a21da3SMarco Felsch *fmt = tc358746_def_fmt; 75080a21da3SMarco Felsch 75180a21da3SMarco Felsch fmt = v4l2_subdev_get_pad_format(sd, state, TC358746_SOURCE); 75280a21da3SMarco Felsch *fmt = tc358746_def_fmt; 75380a21da3SMarco Felsch fmt->code = tc358746_src_mbus_code(tc358746_def_fmt.code); 75480a21da3SMarco Felsch 75580a21da3SMarco Felsch return 0; 75680a21da3SMarco Felsch } 75780a21da3SMarco Felsch 75880a21da3SMarco Felsch static int tc358746_enum_mbus_code(struct v4l2_subdev *sd, 75980a21da3SMarco Felsch struct v4l2_subdev_state *sd_state, 76080a21da3SMarco Felsch struct v4l2_subdev_mbus_code_enum *code) 76180a21da3SMarco Felsch { 76280a21da3SMarco Felsch const struct tc358746_format *fmt; 76380a21da3SMarco Felsch 76480a21da3SMarco Felsch fmt = tc358746_get_format_by_idx(code->pad, code->index); 76580a21da3SMarco Felsch if (IS_ERR(fmt)) 76680a21da3SMarco Felsch return PTR_ERR(fmt); 76780a21da3SMarco Felsch 76880a21da3SMarco Felsch code->code = fmt->code; 76980a21da3SMarco Felsch 77080a21da3SMarco Felsch return 0; 77180a21da3SMarco Felsch } 77280a21da3SMarco Felsch 77380a21da3SMarco Felsch static int tc358746_set_fmt(struct v4l2_subdev *sd, 77480a21da3SMarco Felsch struct v4l2_subdev_state *sd_state, 77580a21da3SMarco Felsch struct v4l2_subdev_format *format) 77680a21da3SMarco Felsch { 77780a21da3SMarco Felsch struct v4l2_mbus_framefmt *src_fmt, *sink_fmt; 77880a21da3SMarco Felsch const struct tc358746_format *fmt; 77980a21da3SMarco Felsch 78080a21da3SMarco Felsch /* Source follows the sink */ 78180a21da3SMarco Felsch if (format->pad == TC358746_SOURCE) 78280a21da3SMarco Felsch return v4l2_subdev_get_fmt(sd, sd_state, format); 78380a21da3SMarco Felsch 78480a21da3SMarco Felsch sink_fmt = v4l2_subdev_get_pad_format(sd, sd_state, TC358746_SINK); 78580a21da3SMarco Felsch 78680a21da3SMarco Felsch fmt = tc358746_get_format_by_code(format->pad, format->format.code); 78780a21da3SMarco Felsch if (IS_ERR(fmt)) 78880a21da3SMarco Felsch fmt = tc358746_get_format_by_code(format->pad, tc358746_def_fmt.code); 78980a21da3SMarco Felsch 79080a21da3SMarco Felsch format->format.code = fmt->code; 79180a21da3SMarco Felsch format->format.field = V4L2_FIELD_NONE; 79280a21da3SMarco Felsch 79380a21da3SMarco Felsch dev_dbg(sd->dev, "Update format: %ux%u code:0x%x -> %ux%u code:0x%x", 79480a21da3SMarco Felsch sink_fmt->width, sink_fmt->height, sink_fmt->code, 79580a21da3SMarco Felsch format->format.width, format->format.height, format->format.code); 79680a21da3SMarco Felsch 79780a21da3SMarco Felsch *sink_fmt = format->format; 79880a21da3SMarco Felsch 79980a21da3SMarco Felsch src_fmt = v4l2_subdev_get_pad_format(sd, sd_state, TC358746_SOURCE); 80080a21da3SMarco Felsch *src_fmt = *sink_fmt; 80180a21da3SMarco Felsch src_fmt->code = tc358746_src_mbus_code(sink_fmt->code); 80280a21da3SMarco Felsch 80380a21da3SMarco Felsch return 0; 80480a21da3SMarco Felsch } 80580a21da3SMarco Felsch 80680a21da3SMarco Felsch static unsigned long tc358746_find_pll_settings(struct tc358746 *tc358746, 80780a21da3SMarco Felsch unsigned long refclk, 80880a21da3SMarco Felsch unsigned long fout) 80980a21da3SMarco Felsch 81080a21da3SMarco Felsch { 81180a21da3SMarco Felsch struct device *dev = tc358746->sd.dev; 81280a21da3SMarco Felsch unsigned long best_freq = 0; 81380a21da3SMarco Felsch u32 min_delta = 0xffffffff; 81480a21da3SMarco Felsch u16 prediv_max = 17; 81580a21da3SMarco Felsch u16 prediv_min = 1; 81680a21da3SMarco Felsch u16 m_best, mul; 81780a21da3SMarco Felsch u16 p_best, p; 81880a21da3SMarco Felsch u8 postdiv; 81980a21da3SMarco Felsch 82080a21da3SMarco Felsch if (fout > 1000 * HZ_PER_MHZ) { 82180a21da3SMarco Felsch dev_err(dev, "HS-Clock above 1 Ghz are not supported\n"); 82280a21da3SMarco Felsch return 0; 82380a21da3SMarco Felsch } 82480a21da3SMarco Felsch 82580a21da3SMarco Felsch if (fout >= 500 * HZ_PER_MHZ) 82680a21da3SMarco Felsch postdiv = 1; 82780a21da3SMarco Felsch else if (fout >= 250 * HZ_PER_MHZ) 82880a21da3SMarco Felsch postdiv = 2; 82980a21da3SMarco Felsch else if (fout >= 125 * HZ_PER_MHZ) 83080a21da3SMarco Felsch postdiv = 4; 83180a21da3SMarco Felsch else 83280a21da3SMarco Felsch postdiv = 8; 83380a21da3SMarco Felsch 83480a21da3SMarco Felsch for (p = prediv_min; p <= prediv_max; p++) { 83580a21da3SMarco Felsch unsigned long delta, fin; 83680a21da3SMarco Felsch u64 tmp; 83780a21da3SMarco Felsch 83880a21da3SMarco Felsch fin = DIV_ROUND_CLOSEST(refclk, p); 83980a21da3SMarco Felsch if (fin < 4 * HZ_PER_MHZ || fin > 40 * HZ_PER_MHZ) 84080a21da3SMarco Felsch continue; 84180a21da3SMarco Felsch 84280a21da3SMarco Felsch tmp = fout * p * postdiv; 84380a21da3SMarco Felsch do_div(tmp, fin); 84480a21da3SMarco Felsch mul = tmp; 84580a21da3SMarco Felsch if (mul > 511) 84680a21da3SMarco Felsch continue; 84780a21da3SMarco Felsch 84880a21da3SMarco Felsch tmp = mul * fin; 84980a21da3SMarco Felsch do_div(tmp, p * postdiv); 85080a21da3SMarco Felsch 85180a21da3SMarco Felsch delta = abs(fout - tmp); 85280a21da3SMarco Felsch if (delta < min_delta) { 85380a21da3SMarco Felsch p_best = p; 85480a21da3SMarco Felsch m_best = mul; 85580a21da3SMarco Felsch min_delta = delta; 85680a21da3SMarco Felsch best_freq = tmp; 85780a21da3SMarco Felsch }; 85880a21da3SMarco Felsch 85980a21da3SMarco Felsch if (delta == 0) 86080a21da3SMarco Felsch break; 86180a21da3SMarco Felsch }; 86280a21da3SMarco Felsch 86380a21da3SMarco Felsch if (!best_freq) { 86480a21da3SMarco Felsch dev_err(dev, "Failed find PLL frequency\n"); 86580a21da3SMarco Felsch return 0; 86680a21da3SMarco Felsch } 86780a21da3SMarco Felsch 86880a21da3SMarco Felsch tc358746->pll_post_div = postdiv; 86980a21da3SMarco Felsch tc358746->pll_pre_div = p_best; 87080a21da3SMarco Felsch tc358746->pll_mul = m_best; 87180a21da3SMarco Felsch 87280a21da3SMarco Felsch if (best_freq != fout) 87380a21da3SMarco Felsch dev_warn(dev, "Request PLL freq:%lu, found PLL freq:%lu\n", 87480a21da3SMarco Felsch fout, best_freq); 87580a21da3SMarco Felsch 87680a21da3SMarco Felsch dev_dbg(dev, "Found PLL settings: freq:%lu prediv:%u multi:%u postdiv:%u\n", 87780a21da3SMarco Felsch best_freq, p_best, m_best, postdiv); 87880a21da3SMarco Felsch 87980a21da3SMarco Felsch return best_freq; 88080a21da3SMarco Felsch } 88180a21da3SMarco Felsch 88280a21da3SMarco Felsch #define TC358746_PRECISION 10 88380a21da3SMarco Felsch 88480a21da3SMarco Felsch static int 88580a21da3SMarco Felsch tc358746_link_validate(struct v4l2_subdev *sd, struct media_link *link, 88680a21da3SMarco Felsch struct v4l2_subdev_format *source_fmt, 88780a21da3SMarco Felsch struct v4l2_subdev_format *sink_fmt) 88880a21da3SMarco Felsch { 88980a21da3SMarco Felsch struct tc358746 *tc358746 = to_tc358746(sd); 89080a21da3SMarco Felsch unsigned long csi_bitrate, source_bitrate; 89180a21da3SMarco Felsch struct v4l2_subdev_state *sink_state; 89280a21da3SMarco Felsch struct v4l2_mbus_framefmt *mbusfmt; 89380a21da3SMarco Felsch const struct tc358746_format *fmt; 89480a21da3SMarco Felsch unsigned int fifo_sz, tmp, n; 89580a21da3SMarco Felsch struct v4l2_subdev *source; 89680a21da3SMarco Felsch s64 source_link_freq; 89780a21da3SMarco Felsch int err; 89880a21da3SMarco Felsch 89980a21da3SMarco Felsch err = v4l2_subdev_link_validate_default(sd, link, source_fmt, sink_fmt); 90080a21da3SMarco Felsch if (err) 90180a21da3SMarco Felsch return err; 90280a21da3SMarco Felsch 90380a21da3SMarco Felsch sink_state = v4l2_subdev_lock_and_get_active_state(sd); 90480a21da3SMarco Felsch mbusfmt = v4l2_subdev_get_pad_format(sd, sink_state, TC358746_SINK); 90580a21da3SMarco Felsch 90680a21da3SMarco Felsch /* Check the FIFO settings */ 90780a21da3SMarco Felsch fmt = tc358746_get_format_by_code(TC358746_SINK, mbusfmt->code); 90880a21da3SMarco Felsch 90980a21da3SMarco Felsch source = media_entity_to_v4l2_subdev(link->source->entity); 91080a21da3SMarco Felsch source_link_freq = v4l2_get_link_freq(source->ctrl_handler, 0, 0); 91180a21da3SMarco Felsch if (source_link_freq <= 0) { 91280a21da3SMarco Felsch dev_err(tc358746->sd.dev, 91380a21da3SMarco Felsch "Failed to query or invalid source link frequency\n"); 91480a21da3SMarco Felsch v4l2_subdev_unlock_state(sink_state); 91580a21da3SMarco Felsch /* Return -EINVAL in case of source_link_freq is 0 */ 91680a21da3SMarco Felsch return source_link_freq ? : -EINVAL; 91780a21da3SMarco Felsch } 91880a21da3SMarco Felsch source_bitrate = source_link_freq * fmt->bus_width; 91980a21da3SMarco Felsch 92080a21da3SMarco Felsch csi_bitrate = tc358746->dphy_cfg.lanes * tc358746->pll_rate; 92180a21da3SMarco Felsch 92280a21da3SMarco Felsch dev_dbg(tc358746->sd.dev, 92380a21da3SMarco Felsch "Fifo settings params: source-bitrate:%lu csi-bitrate:%lu", 92480a21da3SMarco Felsch source_bitrate, csi_bitrate); 92580a21da3SMarco Felsch 92680a21da3SMarco Felsch /* Avoid possible FIFO overflows */ 92780a21da3SMarco Felsch if (csi_bitrate < source_bitrate) { 92880a21da3SMarco Felsch v4l2_subdev_unlock_state(sink_state); 92980a21da3SMarco Felsch return -EINVAL; 93080a21da3SMarco Felsch } 93180a21da3SMarco Felsch 93280a21da3SMarco Felsch /* Best case */ 93380a21da3SMarco Felsch if (csi_bitrate == source_bitrate) { 93480a21da3SMarco Felsch fifo_sz = TC358746_VB_DEFAULT_SIZE; 93580a21da3SMarco Felsch tc358746->vb_size = TC358746_VB_DEFAULT_SIZE; 93680a21da3SMarco Felsch goto out; 93780a21da3SMarco Felsch } 93880a21da3SMarco Felsch 93980a21da3SMarco Felsch /* 94080a21da3SMarco Felsch * Avoid possible FIFO underflow in case of 94180a21da3SMarco Felsch * csi_bitrate > source_bitrate. For such case the chip has a internal 94280a21da3SMarco Felsch * fifo which can be used to delay the line output. 94380a21da3SMarco Felsch * 94480a21da3SMarco Felsch * Fifo size calculation (excluding precision): 94580a21da3SMarco Felsch * 94680a21da3SMarco Felsch * fifo-sz, image-width - in bits 94780a21da3SMarco Felsch * sbr - source_bitrate in bits/s 94880a21da3SMarco Felsch * csir - csi_bitrate in bits/s 94980a21da3SMarco Felsch * 95080a21da3SMarco Felsch * image-width / csir >= (image-width - fifo-sz) / sbr 95180a21da3SMarco Felsch * image-width * sbr / csir >= image-width - fifo-sz 95280a21da3SMarco Felsch * fifo-sz >= image-width - image-width * sbr / csir; with n = csir/sbr 95380a21da3SMarco Felsch * fifo-sz >= image-width - image-width / n 95480a21da3SMarco Felsch */ 95580a21da3SMarco Felsch 95680a21da3SMarco Felsch source_bitrate /= TC358746_PRECISION; 95780a21da3SMarco Felsch n = csi_bitrate / source_bitrate; 95880a21da3SMarco Felsch tmp = (mbusfmt->width * TC358746_PRECISION) / n; 95980a21da3SMarco Felsch fifo_sz = mbusfmt->width - tmp; 96080a21da3SMarco Felsch fifo_sz *= fmt->bpp; 96180a21da3SMarco Felsch tc358746->vb_size = round_up(fifo_sz, 32); 96280a21da3SMarco Felsch 96380a21da3SMarco Felsch out: 96480a21da3SMarco Felsch dev_dbg(tc358746->sd.dev, 96580a21da3SMarco Felsch "Found FIFO size[bits]:%u -> aligned to size[bits]:%u\n", 96680a21da3SMarco Felsch fifo_sz, tc358746->vb_size); 96780a21da3SMarco Felsch 96880a21da3SMarco Felsch v4l2_subdev_unlock_state(sink_state); 96980a21da3SMarco Felsch 97080a21da3SMarco Felsch return tc358746->vb_size > TC358746_VB_MAX_SIZE ? -EINVAL : 0; 97180a21da3SMarco Felsch } 97280a21da3SMarco Felsch 97380a21da3SMarco Felsch static int tc358746_get_mbus_config(struct v4l2_subdev *sd, unsigned int pad, 97480a21da3SMarco Felsch struct v4l2_mbus_config *config) 97580a21da3SMarco Felsch { 97680a21da3SMarco Felsch struct tc358746 *tc358746 = to_tc358746(sd); 97780a21da3SMarco Felsch 97880a21da3SMarco Felsch if (pad != TC358746_SOURCE) 97980a21da3SMarco Felsch return -EINVAL; 98080a21da3SMarco Felsch 98180a21da3SMarco Felsch config->type = V4L2_MBUS_CSI2_DPHY; 98280a21da3SMarco Felsch config->bus.mipi_csi2 = tc358746->csi_vep.bus.mipi_csi2; 98380a21da3SMarco Felsch 98480a21da3SMarco Felsch return 0; 98580a21da3SMarco Felsch } 98680a21da3SMarco Felsch 98780a21da3SMarco Felsch static int __maybe_unused 98880a21da3SMarco Felsch tc358746_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) 98980a21da3SMarco Felsch { 99080a21da3SMarco Felsch struct tc358746 *tc358746 = to_tc358746(sd); 991*9d33802cSMarco Felsch int err; 99280a21da3SMarco Felsch 99380a21da3SMarco Felsch /* 32-bit registers starting from CLW_DPHYCONTTX */ 99480a21da3SMarco Felsch reg->size = reg->reg < CLW_DPHYCONTTX_REG ? 2 : 4; 99580a21da3SMarco Felsch 99680a21da3SMarco Felsch if (!pm_runtime_get_if_in_use(sd->dev)) 99780a21da3SMarco Felsch return 0; 99880a21da3SMarco Felsch 999*9d33802cSMarco Felsch err = tc358746_read(tc358746, reg->reg, (u32 *)®->val); 100080a21da3SMarco Felsch 100180a21da3SMarco Felsch pm_runtime_mark_last_busy(sd->dev); 100280a21da3SMarco Felsch pm_runtime_put_sync_autosuspend(sd->dev); 100380a21da3SMarco Felsch 1004*9d33802cSMarco Felsch return err; 100580a21da3SMarco Felsch } 100680a21da3SMarco Felsch 100780a21da3SMarco Felsch static int __maybe_unused 100880a21da3SMarco Felsch tc358746_s_register(struct v4l2_subdev *sd, const struct v4l2_dbg_register *reg) 100980a21da3SMarco Felsch { 101080a21da3SMarco Felsch struct tc358746 *tc358746 = to_tc358746(sd); 101180a21da3SMarco Felsch 101280a21da3SMarco Felsch if (!pm_runtime_get_if_in_use(sd->dev)) 101380a21da3SMarco Felsch return 0; 101480a21da3SMarco Felsch 101580a21da3SMarco Felsch tc358746_write(tc358746, (u32)reg->reg, (u32)reg->val); 101680a21da3SMarco Felsch 101780a21da3SMarco Felsch pm_runtime_mark_last_busy(sd->dev); 101880a21da3SMarco Felsch pm_runtime_put_sync_autosuspend(sd->dev); 101980a21da3SMarco Felsch 102080a21da3SMarco Felsch return 0; 102180a21da3SMarco Felsch } 102280a21da3SMarco Felsch 102380a21da3SMarco Felsch static const struct v4l2_subdev_core_ops tc358746_core_ops = { 102480a21da3SMarco Felsch #ifdef CONFIG_VIDEO_ADV_DEBUG 102580a21da3SMarco Felsch .g_register = tc358746_g_register, 102680a21da3SMarco Felsch .s_register = tc358746_s_register, 102780a21da3SMarco Felsch #endif 102880a21da3SMarco Felsch }; 102980a21da3SMarco Felsch 103080a21da3SMarco Felsch static const struct v4l2_subdev_video_ops tc358746_video_ops = { 103180a21da3SMarco Felsch .s_stream = tc358746_s_stream, 103280a21da3SMarco Felsch }; 103380a21da3SMarco Felsch 103480a21da3SMarco Felsch static const struct v4l2_subdev_pad_ops tc358746_pad_ops = { 103580a21da3SMarco Felsch .init_cfg = tc358746_init_cfg, 103680a21da3SMarco Felsch .enum_mbus_code = tc358746_enum_mbus_code, 103780a21da3SMarco Felsch .set_fmt = tc358746_set_fmt, 103880a21da3SMarco Felsch .get_fmt = v4l2_subdev_get_fmt, 103980a21da3SMarco Felsch .link_validate = tc358746_link_validate, 104080a21da3SMarco Felsch .get_mbus_config = tc358746_get_mbus_config, 104180a21da3SMarco Felsch }; 104280a21da3SMarco Felsch 104380a21da3SMarco Felsch static const struct v4l2_subdev_ops tc358746_ops = { 104480a21da3SMarco Felsch .core = &tc358746_core_ops, 104580a21da3SMarco Felsch .video = &tc358746_video_ops, 104680a21da3SMarco Felsch .pad = &tc358746_pad_ops, 104780a21da3SMarco Felsch }; 104880a21da3SMarco Felsch 104980a21da3SMarco Felsch static const struct media_entity_operations tc358746_entity_ops = { 105080a21da3SMarco Felsch .get_fwnode_pad = v4l2_subdev_get_fwnode_pad_1_to_1, 105180a21da3SMarco Felsch .link_validate = v4l2_subdev_link_validate, 105280a21da3SMarco Felsch }; 105380a21da3SMarco Felsch 105480a21da3SMarco Felsch static int tc358746_mclk_enable(struct clk_hw *hw) 105580a21da3SMarco Felsch { 105680a21da3SMarco Felsch struct tc358746 *tc358746 = clk_hw_to_tc358746(hw); 105780a21da3SMarco Felsch unsigned int div; 105880a21da3SMarco Felsch u32 val; 105980a21da3SMarco Felsch int err; 106080a21da3SMarco Felsch 106180a21da3SMarco Felsch div = tc358746->mclk_postdiv / 2; 106280a21da3SMarco Felsch val = MCLK_HIGH(div - 1) | MCLK_LOW(div - 1); 106380a21da3SMarco Felsch dev_dbg(tc358746->sd.dev, "MCLKCTL: %u (0x%x)\n", val, val); 106480a21da3SMarco Felsch err = tc358746_write(tc358746, MCLKCTL_REG, val); 106580a21da3SMarco Felsch if (err) 106680a21da3SMarco Felsch return err; 106780a21da3SMarco Felsch 106880a21da3SMarco Felsch if (tc358746->mclk_prediv == 8) 106980a21da3SMarco Felsch val = MCLKDIV(MCLKDIV_8); 107080a21da3SMarco Felsch else if (tc358746->mclk_prediv == 4) 107180a21da3SMarco Felsch val = MCLKDIV(MCLKDIV_4); 107280a21da3SMarco Felsch else 107380a21da3SMarco Felsch val = MCLKDIV(MCLKDIV_2); 107480a21da3SMarco Felsch 107580a21da3SMarco Felsch dev_dbg(tc358746->sd.dev, "CLKCTL[MCLKDIV]: %u (0x%x)\n", val, val); 107680a21da3SMarco Felsch 107780a21da3SMarco Felsch return tc358746_update_bits(tc358746, CLKCTL_REG, MCLKDIV_MASK, val); 107880a21da3SMarco Felsch } 107980a21da3SMarco Felsch 108080a21da3SMarco Felsch static void tc358746_mclk_disable(struct clk_hw *hw) 108180a21da3SMarco Felsch { 108280a21da3SMarco Felsch struct tc358746 *tc358746 = clk_hw_to_tc358746(hw); 108380a21da3SMarco Felsch 108480a21da3SMarco Felsch tc358746_write(tc358746, MCLKCTL_REG, 0); 108580a21da3SMarco Felsch } 108680a21da3SMarco Felsch 108780a21da3SMarco Felsch static long 108880a21da3SMarco Felsch tc358746_find_mclk_settings(struct tc358746 *tc358746, unsigned long mclk_rate) 108980a21da3SMarco Felsch { 109080a21da3SMarco Felsch unsigned long pll_rate = tc358746->pll_rate; 109180a21da3SMarco Felsch const unsigned char prediv[] = { 2, 4, 8 }; 109280a21da3SMarco Felsch unsigned int mclk_prediv, mclk_postdiv; 109380a21da3SMarco Felsch struct device *dev = tc358746->sd.dev; 109480a21da3SMarco Felsch unsigned int postdiv, mclkdiv; 109580a21da3SMarco Felsch unsigned long best_mclk_rate; 109680a21da3SMarco Felsch unsigned int i; 109780a21da3SMarco Felsch 109880a21da3SMarco Felsch /* 109980a21da3SMarco Felsch * MCLK-Div 110080a21da3SMarco Felsch * -------------------´`--------------------- 110180a21da3SMarco Felsch * ´ ` 110280a21da3SMarco Felsch * +-------------+ +------------------------+ 110380a21da3SMarco Felsch * | MCLK-PreDiv | | MCLK-PostDiv | 110480a21da3SMarco Felsch * PLL --> | (2/4/8) | --> | (mclk_low + mclk_high) | --> MCLK 110580a21da3SMarco Felsch * +-------------+ +------------------------+ 110680a21da3SMarco Felsch * 110780a21da3SMarco Felsch * The register value of mclk_low/high is mclk_low/high+1, i.e.: 110880a21da3SMarco Felsch * mclk_low/high = 1 --> 2 MCLK-Ref Counts 110980a21da3SMarco Felsch * mclk_low/high = 255 --> 256 MCLK-Ref Counts == max. 111080a21da3SMarco Felsch * If mclk_low and mclk_high are 0 then MCLK is disabled. 111180a21da3SMarco Felsch * 111280a21da3SMarco Felsch * Keep it simple and support 50/50 duty cycles only for now, 111380a21da3SMarco Felsch * so the calc will be: 111480a21da3SMarco Felsch * 111580a21da3SMarco Felsch * MCLK = PLL / (MCLK-PreDiv * 2 * MCLK-PostDiv) 111680a21da3SMarco Felsch */ 111780a21da3SMarco Felsch 111880a21da3SMarco Felsch if (mclk_rate == tc358746->mclk_rate) 111980a21da3SMarco Felsch return mclk_rate; 112080a21da3SMarco Felsch 112180a21da3SMarco Felsch /* Highest possible rate */ 112280a21da3SMarco Felsch mclkdiv = pll_rate / mclk_rate; 112380a21da3SMarco Felsch if (mclkdiv <= 8) { 112480a21da3SMarco Felsch mclk_prediv = 2; 112580a21da3SMarco Felsch mclk_postdiv = 4; 112680a21da3SMarco Felsch best_mclk_rate = pll_rate / (2 * 4); 112780a21da3SMarco Felsch goto out; 112880a21da3SMarco Felsch } 112980a21da3SMarco Felsch 113080a21da3SMarco Felsch /* First check the prediv */ 113180a21da3SMarco Felsch for (i = 0; i < ARRAY_SIZE(prediv); i++) { 113280a21da3SMarco Felsch postdiv = mclkdiv / prediv[i]; 113380a21da3SMarco Felsch 113480a21da3SMarco Felsch if (postdiv % 2) 113580a21da3SMarco Felsch continue; 113680a21da3SMarco Felsch 113780a21da3SMarco Felsch if (postdiv >= 4 && postdiv <= 512) { 113880a21da3SMarco Felsch mclk_prediv = prediv[i]; 113980a21da3SMarco Felsch mclk_postdiv = postdiv; 114080a21da3SMarco Felsch best_mclk_rate = pll_rate / (prediv[i] * postdiv); 114180a21da3SMarco Felsch goto out; 114280a21da3SMarco Felsch } 114380a21da3SMarco Felsch } 114480a21da3SMarco Felsch 114580a21da3SMarco Felsch /* No suitable prediv found, so try to adjust the postdiv */ 114680a21da3SMarco Felsch for (postdiv = 4; postdiv <= 512; postdiv += 2) { 114780a21da3SMarco Felsch unsigned int pre; 114880a21da3SMarco Felsch 114980a21da3SMarco Felsch pre = mclkdiv / postdiv; 115080a21da3SMarco Felsch if (pre == 2 || pre == 4 || pre == 8) { 115180a21da3SMarco Felsch mclk_prediv = pre; 115280a21da3SMarco Felsch mclk_postdiv = postdiv; 115380a21da3SMarco Felsch best_mclk_rate = pll_rate / (pre * postdiv); 115480a21da3SMarco Felsch goto out; 115580a21da3SMarco Felsch } 115680a21da3SMarco Felsch } 115780a21da3SMarco Felsch 115880a21da3SMarco Felsch /* The MCLK <-> PLL gap is to high -> use largest possible div */ 115980a21da3SMarco Felsch mclk_prediv = 8; 116080a21da3SMarco Felsch mclk_postdiv = 512; 116180a21da3SMarco Felsch best_mclk_rate = pll_rate / (8 * 512); 116280a21da3SMarco Felsch 116380a21da3SMarco Felsch out: 116480a21da3SMarco Felsch tc358746->mclk_prediv = mclk_prediv; 116580a21da3SMarco Felsch tc358746->mclk_postdiv = mclk_postdiv; 116680a21da3SMarco Felsch tc358746->mclk_rate = best_mclk_rate; 116780a21da3SMarco Felsch 116880a21da3SMarco Felsch if (best_mclk_rate != mclk_rate) 116980a21da3SMarco Felsch dev_warn(dev, "Request MCLK freq:%lu, found MCLK freq:%lu\n", 117080a21da3SMarco Felsch mclk_rate, best_mclk_rate); 117180a21da3SMarco Felsch 117280a21da3SMarco Felsch dev_dbg(dev, "Found MCLK settings: freq:%lu prediv:%u postdiv:%u\n", 117380a21da3SMarco Felsch best_mclk_rate, mclk_prediv, mclk_postdiv); 117480a21da3SMarco Felsch 117580a21da3SMarco Felsch return best_mclk_rate; 117680a21da3SMarco Felsch } 117780a21da3SMarco Felsch 117880a21da3SMarco Felsch static unsigned long 117980a21da3SMarco Felsch tc358746_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) 118080a21da3SMarco Felsch { 118180a21da3SMarco Felsch struct tc358746 *tc358746 = clk_hw_to_tc358746(hw); 118280a21da3SMarco Felsch unsigned int prediv, postdiv; 118380a21da3SMarco Felsch u32 val; 118480a21da3SMarco Felsch int err; 118580a21da3SMarco Felsch 118680a21da3SMarco Felsch err = tc358746_read(tc358746, MCLKCTL_REG, &val); 118780a21da3SMarco Felsch if (err) 118880a21da3SMarco Felsch return 0; 118980a21da3SMarco Felsch 119080a21da3SMarco Felsch postdiv = FIELD_GET(MCLK_LOW_MASK, val) + 1; 119180a21da3SMarco Felsch postdiv += FIELD_GET(MCLK_HIGH_MASK, val) + 1; 119280a21da3SMarco Felsch 119380a21da3SMarco Felsch err = tc358746_read(tc358746, CLKCTL_REG, &val); 119480a21da3SMarco Felsch if (err) 119580a21da3SMarco Felsch return 0; 119680a21da3SMarco Felsch 119780a21da3SMarco Felsch prediv = FIELD_GET(MCLKDIV_MASK, val); 119880a21da3SMarco Felsch if (prediv == MCLKDIV_8) 119980a21da3SMarco Felsch prediv = 8; 120080a21da3SMarco Felsch else if (prediv == MCLKDIV_4) 120180a21da3SMarco Felsch prediv = 4; 120280a21da3SMarco Felsch else 120380a21da3SMarco Felsch prediv = 2; 120480a21da3SMarco Felsch 120580a21da3SMarco Felsch return tc358746->pll_rate / (prediv * postdiv); 120680a21da3SMarco Felsch } 120780a21da3SMarco Felsch 120880a21da3SMarco Felsch static long tc358746_mclk_round_rate(struct clk_hw *hw, unsigned long rate, 120980a21da3SMarco Felsch unsigned long *parent_rate) 121080a21da3SMarco Felsch { 121180a21da3SMarco Felsch struct tc358746 *tc358746 = clk_hw_to_tc358746(hw); 121280a21da3SMarco Felsch 121380a21da3SMarco Felsch *parent_rate = tc358746->pll_rate; 121480a21da3SMarco Felsch 121580a21da3SMarco Felsch return tc358746_find_mclk_settings(tc358746, rate); 121680a21da3SMarco Felsch } 121780a21da3SMarco Felsch 121880a21da3SMarco Felsch static int tc358746_mclk_set_rate(struct clk_hw *hw, unsigned long rate, 121980a21da3SMarco Felsch unsigned long parent_rate) 122080a21da3SMarco Felsch { 122180a21da3SMarco Felsch struct tc358746 *tc358746 = clk_hw_to_tc358746(hw); 122280a21da3SMarco Felsch 122380a21da3SMarco Felsch tc358746_find_mclk_settings(tc358746, rate); 122480a21da3SMarco Felsch 122580a21da3SMarco Felsch return tc358746_mclk_enable(hw); 122680a21da3SMarco Felsch } 122780a21da3SMarco Felsch 122880a21da3SMarco Felsch static const struct clk_ops tc358746_mclk_ops = { 122980a21da3SMarco Felsch .enable = tc358746_mclk_enable, 123080a21da3SMarco Felsch .disable = tc358746_mclk_disable, 123180a21da3SMarco Felsch .recalc_rate = tc358746_recalc_rate, 123280a21da3SMarco Felsch .round_rate = tc358746_mclk_round_rate, 123380a21da3SMarco Felsch .set_rate = tc358746_mclk_set_rate, 123480a21da3SMarco Felsch }; 123580a21da3SMarco Felsch 123680a21da3SMarco Felsch static int tc358746_setup_mclk_provider(struct tc358746 *tc358746) 123780a21da3SMarco Felsch { 123880a21da3SMarco Felsch struct clk_init_data mclk_initdata = { }; 123980a21da3SMarco Felsch struct device *dev = tc358746->sd.dev; 124080a21da3SMarco Felsch const char *mclk_name; 124180a21da3SMarco Felsch int err; 124280a21da3SMarco Felsch 124380a21da3SMarco Felsch /* MCLK clk provider support is optional */ 124480a21da3SMarco Felsch if (!device_property_present(dev, "#clock-cells")) 124580a21da3SMarco Felsch return 0; 124680a21da3SMarco Felsch 124780a21da3SMarco Felsch /* Init to highest possibel MCLK */ 124880a21da3SMarco Felsch tc358746->mclk_postdiv = 512; 124980a21da3SMarco Felsch tc358746->mclk_prediv = 8; 125080a21da3SMarco Felsch 125180a21da3SMarco Felsch mclk_name = "tc358746-mclk"; 125280a21da3SMarco Felsch device_property_read_string(dev, "clock-output-names", &mclk_name); 125380a21da3SMarco Felsch 125480a21da3SMarco Felsch mclk_initdata.name = mclk_name; 125580a21da3SMarco Felsch mclk_initdata.ops = &tc358746_mclk_ops; 125680a21da3SMarco Felsch tc358746->mclk_hw.init = &mclk_initdata; 125780a21da3SMarco Felsch 125880a21da3SMarco Felsch err = devm_clk_hw_register(dev, &tc358746->mclk_hw); 125980a21da3SMarco Felsch if (err) { 126080a21da3SMarco Felsch dev_err(dev, "Failed to register mclk provider\n"); 126180a21da3SMarco Felsch return err; 126280a21da3SMarco Felsch } 126380a21da3SMarco Felsch 126480a21da3SMarco Felsch err = devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get, 126580a21da3SMarco Felsch &tc358746->mclk_hw); 126680a21da3SMarco Felsch if (err) 126780a21da3SMarco Felsch dev_err(dev, "Failed to add mclk provider\n"); 126880a21da3SMarco Felsch 126980a21da3SMarco Felsch return err; 127080a21da3SMarco Felsch } 127180a21da3SMarco Felsch 127280a21da3SMarco Felsch static int 127380a21da3SMarco Felsch tc358746_init_subdev(struct tc358746 *tc358746, struct i2c_client *client) 127480a21da3SMarco Felsch { 127580a21da3SMarco Felsch struct v4l2_subdev *sd = &tc358746->sd; 127680a21da3SMarco Felsch int err; 127780a21da3SMarco Felsch 127880a21da3SMarco Felsch v4l2_i2c_subdev_init(sd, client, &tc358746_ops); 127980a21da3SMarco Felsch sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; 128080a21da3SMarco Felsch sd->entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; 128180a21da3SMarco Felsch sd->entity.ops = &tc358746_entity_ops; 128280a21da3SMarco Felsch 128380a21da3SMarco Felsch tc358746->pads[TC358746_SINK].flags = MEDIA_PAD_FL_SINK; 128480a21da3SMarco Felsch tc358746->pads[TC358746_SOURCE].flags = MEDIA_PAD_FL_SOURCE; 128580a21da3SMarco Felsch err = media_entity_pads_init(&sd->entity, TC358746_NR_PADS, 128680a21da3SMarco Felsch tc358746->pads); 128780a21da3SMarco Felsch if (err) 128880a21da3SMarco Felsch return err; 128980a21da3SMarco Felsch 129080a21da3SMarco Felsch err = v4l2_subdev_init_finalize(sd); 129180a21da3SMarco Felsch if (err) 129280a21da3SMarco Felsch media_entity_cleanup(&sd->entity); 129380a21da3SMarco Felsch 129480a21da3SMarco Felsch return err; 129580a21da3SMarco Felsch } 129680a21da3SMarco Felsch 129780a21da3SMarco Felsch static int 129880a21da3SMarco Felsch tc358746_init_output_port(struct tc358746 *tc358746, unsigned long refclk) 129980a21da3SMarco Felsch { 130080a21da3SMarco Felsch struct device *dev = tc358746->sd.dev; 130180a21da3SMarco Felsch struct v4l2_fwnode_endpoint *vep; 130280a21da3SMarco Felsch unsigned long csi_link_rate; 130380a21da3SMarco Felsch struct fwnode_handle *ep; 130480a21da3SMarco Felsch unsigned char csi_lanes; 130580a21da3SMarco Felsch int err; 130680a21da3SMarco Felsch 130780a21da3SMarco Felsch ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), TC358746_SOURCE, 130880a21da3SMarco Felsch 0, 0); 130980a21da3SMarco Felsch if (!ep) { 131080a21da3SMarco Felsch dev_err(dev, "Missing endpoint node\n"); 131180a21da3SMarco Felsch return -EINVAL; 131280a21da3SMarco Felsch } 131380a21da3SMarco Felsch 131480a21da3SMarco Felsch /* Currently we only support 'parallel in' -> 'csi out' */ 131580a21da3SMarco Felsch vep = &tc358746->csi_vep; 131680a21da3SMarco Felsch vep->bus_type = V4L2_MBUS_CSI2_DPHY; 131780a21da3SMarco Felsch err = v4l2_fwnode_endpoint_alloc_parse(ep, vep); 131880a21da3SMarco Felsch fwnode_handle_put(ep); 131980a21da3SMarco Felsch if (err) { 132080a21da3SMarco Felsch dev_err(dev, "Failed to parse source endpoint\n"); 132180a21da3SMarco Felsch return err; 132280a21da3SMarco Felsch } 132380a21da3SMarco Felsch 132480a21da3SMarco Felsch csi_lanes = vep->bus.mipi_csi2.num_data_lanes; 132580a21da3SMarco Felsch if (csi_lanes == 0 || csi_lanes > 4 || 132680a21da3SMarco Felsch vep->nr_of_link_frequencies == 0) { 132780a21da3SMarco Felsch dev_err(dev, "error: Invalid CSI-2 settings\n"); 132880a21da3SMarco Felsch err = -EINVAL; 132980a21da3SMarco Felsch goto err; 133080a21da3SMarco Felsch } 133180a21da3SMarco Felsch 133280a21da3SMarco Felsch /* TODO: Add support to handle multiple link frequencies */ 133380a21da3SMarco Felsch csi_link_rate = (unsigned long)vep->link_frequencies[0]; 133480a21da3SMarco Felsch tc358746->pll_rate = tc358746_find_pll_settings(tc358746, refclk, 133580a21da3SMarco Felsch csi_link_rate * 2); 133680a21da3SMarco Felsch if (!tc358746->pll_rate) { 133780a21da3SMarco Felsch err = -EINVAL; 133880a21da3SMarco Felsch goto err; 133980a21da3SMarco Felsch } 134080a21da3SMarco Felsch 134180a21da3SMarco Felsch err = phy_mipi_dphy_get_default_config_for_hsclk(tc358746->pll_rate, 134280a21da3SMarco Felsch csi_lanes, &tc358746->dphy_cfg); 134380a21da3SMarco Felsch if (err) 134480a21da3SMarco Felsch goto err; 134580a21da3SMarco Felsch 134680a21da3SMarco Felsch tc358746->vb_size = TC358746_VB_DEFAULT_SIZE; 134780a21da3SMarco Felsch 134880a21da3SMarco Felsch return 0; 134980a21da3SMarco Felsch 135080a21da3SMarco Felsch err: 135180a21da3SMarco Felsch v4l2_fwnode_endpoint_free(vep); 135280a21da3SMarco Felsch 135380a21da3SMarco Felsch return err; 135480a21da3SMarco Felsch } 135580a21da3SMarco Felsch 135680a21da3SMarco Felsch static int tc358746_init_hw(struct tc358746 *tc358746) 135780a21da3SMarco Felsch { 135880a21da3SMarco Felsch struct device *dev = tc358746->sd.dev; 135980a21da3SMarco Felsch unsigned int chipid; 136080a21da3SMarco Felsch u32 val; 136180a21da3SMarco Felsch int err; 136280a21da3SMarco Felsch 136380a21da3SMarco Felsch err = pm_runtime_resume_and_get(dev); 136480a21da3SMarco Felsch if (err < 0) { 136580a21da3SMarco Felsch dev_err(dev, "Failed to resume the device\n"); 136680a21da3SMarco Felsch return err; 136780a21da3SMarco Felsch } 136880a21da3SMarco Felsch 136980a21da3SMarco Felsch /* Ensure that CSI interface is put into LP-11 state */ 137080a21da3SMarco Felsch err = tc358746_sw_reset(tc358746); 137180a21da3SMarco Felsch if (err) { 137280a21da3SMarco Felsch pm_runtime_put_sync(dev); 137380a21da3SMarco Felsch dev_err(dev, "Failed to reset the device\n"); 137480a21da3SMarco Felsch return err; 137580a21da3SMarco Felsch } 137680a21da3SMarco Felsch 137780a21da3SMarco Felsch err = tc358746_read(tc358746, CHIPID_REG, &val); 137880a21da3SMarco Felsch pm_runtime_mark_last_busy(dev); 137980a21da3SMarco Felsch pm_runtime_put_sync_autosuspend(dev); 138080a21da3SMarco Felsch if (err) 138180a21da3SMarco Felsch return -ENODEV; 138280a21da3SMarco Felsch 138380a21da3SMarco Felsch chipid = FIELD_GET(CHIPID, val); 138480a21da3SMarco Felsch if (chipid != 0x44) { 138580a21da3SMarco Felsch dev_err(dev, "Invalid chipid 0x%02x\n", chipid); 138680a21da3SMarco Felsch return -ENODEV; 138780a21da3SMarco Felsch } 138880a21da3SMarco Felsch 138980a21da3SMarco Felsch return 0; 139080a21da3SMarco Felsch } 139180a21da3SMarco Felsch 139280a21da3SMarco Felsch static int tc358746_init_controls(struct tc358746 *tc358746) 139380a21da3SMarco Felsch { 139480a21da3SMarco Felsch u64 *link_frequencies = tc358746->csi_vep.link_frequencies; 139580a21da3SMarco Felsch struct v4l2_ctrl *ctrl; 139680a21da3SMarco Felsch int err; 139780a21da3SMarco Felsch 139880a21da3SMarco Felsch err = v4l2_ctrl_handler_init(&tc358746->ctrl_hdl, 1); 139980a21da3SMarco Felsch if (err) 140080a21da3SMarco Felsch return err; 140180a21da3SMarco Felsch 140280a21da3SMarco Felsch /* 140380a21da3SMarco Felsch * The driver currently supports only one link-frequency, regardless of 140480a21da3SMarco Felsch * the input from the firmware, see: tc358746_init_output_port(). So 140580a21da3SMarco Felsch * report only the first frequency from the array of possible given 140680a21da3SMarco Felsch * frequencies. 140780a21da3SMarco Felsch */ 140880a21da3SMarco Felsch ctrl = v4l2_ctrl_new_int_menu(&tc358746->ctrl_hdl, NULL, 140980a21da3SMarco Felsch V4L2_CID_LINK_FREQ, 0, 0, 141080a21da3SMarco Felsch link_frequencies); 141180a21da3SMarco Felsch if (ctrl) 141280a21da3SMarco Felsch ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; 141380a21da3SMarco Felsch 141480a21da3SMarco Felsch err = tc358746->ctrl_hdl.error; 141580a21da3SMarco Felsch if (err) { 141680a21da3SMarco Felsch v4l2_ctrl_handler_free(&tc358746->ctrl_hdl); 141780a21da3SMarco Felsch return err; 141880a21da3SMarco Felsch } 141980a21da3SMarco Felsch 142080a21da3SMarco Felsch tc358746->sd.ctrl_handler = &tc358746->ctrl_hdl; 142180a21da3SMarco Felsch 142280a21da3SMarco Felsch return 0; 142380a21da3SMarco Felsch } 142480a21da3SMarco Felsch 142580a21da3SMarco Felsch static int tc358746_notify_bound(struct v4l2_async_notifier *notifier, 142680a21da3SMarco Felsch struct v4l2_subdev *sd, 142780a21da3SMarco Felsch struct v4l2_async_subdev *asd) 142880a21da3SMarco Felsch { 142980a21da3SMarco Felsch struct tc358746 *tc358746 = 143080a21da3SMarco Felsch container_of(notifier, struct tc358746, notifier); 143180a21da3SMarco Felsch u32 flags = MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE; 143280a21da3SMarco Felsch struct media_pad *sink = &tc358746->pads[TC358746_SINK]; 143380a21da3SMarco Felsch 143480a21da3SMarco Felsch return v4l2_create_fwnode_links_to_pad(sd, sink, flags); 143580a21da3SMarco Felsch } 143680a21da3SMarco Felsch 143780a21da3SMarco Felsch static const struct v4l2_async_notifier_operations tc358746_notify_ops = { 143880a21da3SMarco Felsch .bound = tc358746_notify_bound, 143980a21da3SMarco Felsch }; 144080a21da3SMarco Felsch 144180a21da3SMarco Felsch static int tc358746_async_register(struct tc358746 *tc358746) 144280a21da3SMarco Felsch { 144380a21da3SMarco Felsch struct v4l2_fwnode_endpoint vep = { 144480a21da3SMarco Felsch .bus_type = V4L2_MBUS_PARALLEL, 144580a21da3SMarco Felsch }; 144680a21da3SMarco Felsch struct v4l2_async_subdev *asd; 144780a21da3SMarco Felsch struct fwnode_handle *ep; 144880a21da3SMarco Felsch int err; 144980a21da3SMarco Felsch 145080a21da3SMarco Felsch ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(tc358746->sd.dev), 145180a21da3SMarco Felsch TC358746_SINK, 0, 0); 145280a21da3SMarco Felsch if (!ep) 145380a21da3SMarco Felsch return -ENOTCONN; 145480a21da3SMarco Felsch 145580a21da3SMarco Felsch err = v4l2_fwnode_endpoint_parse(ep, &vep); 145680a21da3SMarco Felsch if (err) { 145780a21da3SMarco Felsch fwnode_handle_put(ep); 145880a21da3SMarco Felsch return err; 145980a21da3SMarco Felsch } 146080a21da3SMarco Felsch 146180a21da3SMarco Felsch v4l2_async_nf_init(&tc358746->notifier); 146280a21da3SMarco Felsch asd = v4l2_async_nf_add_fwnode_remote(&tc358746->notifier, ep, 146380a21da3SMarco Felsch struct v4l2_async_subdev); 146480a21da3SMarco Felsch fwnode_handle_put(ep); 146580a21da3SMarco Felsch 146680a21da3SMarco Felsch if (IS_ERR(asd)) { 146780a21da3SMarco Felsch err = PTR_ERR(asd); 146880a21da3SMarco Felsch goto err_cleanup; 146980a21da3SMarco Felsch } 147080a21da3SMarco Felsch 147180a21da3SMarco Felsch tc358746->notifier.ops = &tc358746_notify_ops; 147280a21da3SMarco Felsch 147380a21da3SMarco Felsch err = v4l2_async_subdev_nf_register(&tc358746->sd, &tc358746->notifier); 147480a21da3SMarco Felsch if (err) 147580a21da3SMarco Felsch goto err_cleanup; 147680a21da3SMarco Felsch 147780a21da3SMarco Felsch tc358746->sd.fwnode = fwnode_graph_get_endpoint_by_id( 147880a21da3SMarco Felsch dev_fwnode(tc358746->sd.dev), TC358746_SOURCE, 0, 0); 147980a21da3SMarco Felsch 148080a21da3SMarco Felsch err = v4l2_async_register_subdev(&tc358746->sd); 148180a21da3SMarco Felsch if (err) 148280a21da3SMarco Felsch goto err_unregister; 148380a21da3SMarco Felsch 148480a21da3SMarco Felsch return 0; 148580a21da3SMarco Felsch 148680a21da3SMarco Felsch err_unregister: 148780a21da3SMarco Felsch fwnode_handle_put(tc358746->sd.fwnode); 148880a21da3SMarco Felsch v4l2_async_nf_unregister(&tc358746->notifier); 148980a21da3SMarco Felsch err_cleanup: 149080a21da3SMarco Felsch v4l2_async_nf_cleanup(&tc358746->notifier); 149180a21da3SMarco Felsch 149280a21da3SMarco Felsch return err; 149380a21da3SMarco Felsch } 149480a21da3SMarco Felsch 149580a21da3SMarco Felsch static int tc358746_probe(struct i2c_client *client) 149680a21da3SMarco Felsch { 149780a21da3SMarco Felsch struct device *dev = &client->dev; 149880a21da3SMarco Felsch struct tc358746 *tc358746; 149980a21da3SMarco Felsch unsigned long refclk; 150080a21da3SMarco Felsch unsigned int i; 150180a21da3SMarco Felsch int err; 150280a21da3SMarco Felsch 150380a21da3SMarco Felsch tc358746 = devm_kzalloc(&client->dev, sizeof(*tc358746), GFP_KERNEL); 150480a21da3SMarco Felsch if (!tc358746) 150580a21da3SMarco Felsch return -ENOMEM; 150680a21da3SMarco Felsch 150780a21da3SMarco Felsch tc358746->regmap = devm_regmap_init_i2c(client, &tc358746_regmap_config); 150880a21da3SMarco Felsch if (IS_ERR(tc358746->regmap)) 150980a21da3SMarco Felsch return dev_err_probe(dev, PTR_ERR(tc358746->regmap), 151080a21da3SMarco Felsch "Failed to init regmap\n"); 151180a21da3SMarco Felsch 151280a21da3SMarco Felsch tc358746->refclk = devm_clk_get(dev, "refclk"); 151380a21da3SMarco Felsch if (IS_ERR(tc358746->refclk)) 151480a21da3SMarco Felsch return dev_err_probe(dev, PTR_ERR(tc358746->refclk), 151580a21da3SMarco Felsch "Failed to get refclk\n"); 151680a21da3SMarco Felsch 151780a21da3SMarco Felsch err = clk_prepare_enable(tc358746->refclk); 151880a21da3SMarco Felsch if (err) 151980a21da3SMarco Felsch return dev_err_probe(dev, err, 152080a21da3SMarco Felsch "Failed to enable refclk\n"); 152180a21da3SMarco Felsch 152280a21da3SMarco Felsch refclk = clk_get_rate(tc358746->refclk); 152380a21da3SMarco Felsch clk_disable_unprepare(tc358746->refclk); 152480a21da3SMarco Felsch 152580a21da3SMarco Felsch if (refclk < 6 * HZ_PER_MHZ || refclk > 40 * HZ_PER_MHZ) 152680a21da3SMarco Felsch return dev_err_probe(dev, -EINVAL, "Invalid refclk range\n"); 152780a21da3SMarco Felsch 152880a21da3SMarco Felsch for (i = 0; i < ARRAY_SIZE(tc358746_supplies); i++) 152980a21da3SMarco Felsch tc358746->supplies[i].supply = tc358746_supplies[i]; 153080a21da3SMarco Felsch 153180a21da3SMarco Felsch err = devm_regulator_bulk_get(dev, ARRAY_SIZE(tc358746_supplies), 153280a21da3SMarco Felsch tc358746->supplies); 153380a21da3SMarco Felsch if (err) 153480a21da3SMarco Felsch return dev_err_probe(dev, err, "Failed to get supplies\n"); 153580a21da3SMarco Felsch 153680a21da3SMarco Felsch tc358746->reset_gpio = devm_gpiod_get_optional(dev, "reset", 153780a21da3SMarco Felsch GPIOD_OUT_HIGH); 153880a21da3SMarco Felsch if (IS_ERR(tc358746->reset_gpio)) 153980a21da3SMarco Felsch return dev_err_probe(dev, PTR_ERR(tc358746->reset_gpio), 154080a21da3SMarco Felsch "Failed to get reset-gpios\n"); 154180a21da3SMarco Felsch 154280a21da3SMarco Felsch err = tc358746_init_subdev(tc358746, client); 154380a21da3SMarco Felsch if (err) 154480a21da3SMarco Felsch return dev_err_probe(dev, err, "Failed to init subdev\n"); 154580a21da3SMarco Felsch 154680a21da3SMarco Felsch err = tc358746_init_output_port(tc358746, refclk); 154780a21da3SMarco Felsch if (err) 154880a21da3SMarco Felsch goto err_subdev; 154980a21da3SMarco Felsch 155080a21da3SMarco Felsch /* 155180a21da3SMarco Felsch * Keep this order since we need the output port link-frequencies 155280a21da3SMarco Felsch * information. 155380a21da3SMarco Felsch */ 155480a21da3SMarco Felsch err = tc358746_init_controls(tc358746); 155580a21da3SMarco Felsch if (err) 155680a21da3SMarco Felsch goto err_fwnode; 155780a21da3SMarco Felsch 155880a21da3SMarco Felsch dev_set_drvdata(dev, tc358746); 155980a21da3SMarco Felsch 156080a21da3SMarco Felsch /* Set to 1sec to give the stream reconfiguration enough time */ 156180a21da3SMarco Felsch pm_runtime_set_autosuspend_delay(dev, 1000); 156280a21da3SMarco Felsch pm_runtime_use_autosuspend(dev); 156380a21da3SMarco Felsch pm_runtime_enable(dev); 156480a21da3SMarco Felsch 156580a21da3SMarco Felsch err = tc358746_init_hw(tc358746); 156680a21da3SMarco Felsch if (err) 156780a21da3SMarco Felsch goto err_pm; 156880a21da3SMarco Felsch 156980a21da3SMarco Felsch err = tc358746_setup_mclk_provider(tc358746); 157080a21da3SMarco Felsch if (err) 157180a21da3SMarco Felsch goto err_pm; 157280a21da3SMarco Felsch 157380a21da3SMarco Felsch err = tc358746_async_register(tc358746); 157480a21da3SMarco Felsch if (err < 0) 157580a21da3SMarco Felsch goto err_pm; 157680a21da3SMarco Felsch 157780a21da3SMarco Felsch dev_dbg(dev, "%s found @ 0x%x (%s)\n", client->name, 157880a21da3SMarco Felsch client->addr, client->adapter->name); 157980a21da3SMarco Felsch 158080a21da3SMarco Felsch return 0; 158180a21da3SMarco Felsch 158280a21da3SMarco Felsch err_pm: 158380a21da3SMarco Felsch pm_runtime_disable(dev); 158480a21da3SMarco Felsch pm_runtime_set_suspended(dev); 158580a21da3SMarco Felsch pm_runtime_dont_use_autosuspend(dev); 158680a21da3SMarco Felsch v4l2_ctrl_handler_free(&tc358746->ctrl_hdl); 158780a21da3SMarco Felsch err_fwnode: 158880a21da3SMarco Felsch v4l2_fwnode_endpoint_free(&tc358746->csi_vep); 158980a21da3SMarco Felsch err_subdev: 159080a21da3SMarco Felsch v4l2_subdev_cleanup(&tc358746->sd); 159180a21da3SMarco Felsch media_entity_cleanup(&tc358746->sd.entity); 159280a21da3SMarco Felsch 159380a21da3SMarco Felsch return err; 159480a21da3SMarco Felsch } 159580a21da3SMarco Felsch 159680a21da3SMarco Felsch static void tc358746_remove(struct i2c_client *client) 159780a21da3SMarco Felsch { 159880a21da3SMarco Felsch struct v4l2_subdev *sd = i2c_get_clientdata(client); 159980a21da3SMarco Felsch struct tc358746 *tc358746 = to_tc358746(sd); 160080a21da3SMarco Felsch 160180a21da3SMarco Felsch v4l2_subdev_cleanup(sd); 160280a21da3SMarco Felsch v4l2_ctrl_handler_free(&tc358746->ctrl_hdl); 160380a21da3SMarco Felsch v4l2_fwnode_endpoint_free(&tc358746->csi_vep); 160480a21da3SMarco Felsch v4l2_async_nf_unregister(&tc358746->notifier); 160580a21da3SMarco Felsch v4l2_async_nf_cleanup(&tc358746->notifier); 160680a21da3SMarco Felsch fwnode_handle_put(sd->fwnode); 160780a21da3SMarco Felsch v4l2_async_unregister_subdev(sd); 160880a21da3SMarco Felsch media_entity_cleanup(&sd->entity); 160980a21da3SMarco Felsch 161080a21da3SMarco Felsch pm_runtime_disable(sd->dev); 161180a21da3SMarco Felsch pm_runtime_set_suspended(sd->dev); 161280a21da3SMarco Felsch pm_runtime_dont_use_autosuspend(sd->dev); 161380a21da3SMarco Felsch } 161480a21da3SMarco Felsch 161580a21da3SMarco Felsch static int tc358746_suspend(struct device *dev) 161680a21da3SMarco Felsch { 161780a21da3SMarco Felsch struct tc358746 *tc358746 = dev_get_drvdata(dev); 161880a21da3SMarco Felsch int err; 161980a21da3SMarco Felsch 162080a21da3SMarco Felsch clk_disable_unprepare(tc358746->refclk); 162180a21da3SMarco Felsch 162280a21da3SMarco Felsch err = regulator_bulk_disable(ARRAY_SIZE(tc358746_supplies), 162380a21da3SMarco Felsch tc358746->supplies); 162480a21da3SMarco Felsch if (err) 162580a21da3SMarco Felsch clk_prepare_enable(tc358746->refclk); 162680a21da3SMarco Felsch 162780a21da3SMarco Felsch return err; 162880a21da3SMarco Felsch } 162980a21da3SMarco Felsch 163080a21da3SMarco Felsch static int tc358746_resume(struct device *dev) 163180a21da3SMarco Felsch { 163280a21da3SMarco Felsch struct tc358746 *tc358746 = dev_get_drvdata(dev); 163380a21da3SMarco Felsch int err; 163480a21da3SMarco Felsch 163580a21da3SMarco Felsch gpiod_set_value(tc358746->reset_gpio, 1); 163680a21da3SMarco Felsch 163780a21da3SMarco Felsch err = regulator_bulk_enable(ARRAY_SIZE(tc358746_supplies), 163880a21da3SMarco Felsch tc358746->supplies); 163980a21da3SMarco Felsch if (err) 164080a21da3SMarco Felsch return err; 164180a21da3SMarco Felsch 164280a21da3SMarco Felsch /* min. 200ns */ 164380a21da3SMarco Felsch usleep_range(10, 20); 164480a21da3SMarco Felsch 164580a21da3SMarco Felsch gpiod_set_value(tc358746->reset_gpio, 0); 164680a21da3SMarco Felsch 164780a21da3SMarco Felsch err = clk_prepare_enable(tc358746->refclk); 164880a21da3SMarco Felsch if (err) 164980a21da3SMarco Felsch goto err; 165080a21da3SMarco Felsch 165180a21da3SMarco Felsch /* min. 700us ... 1ms */ 165280a21da3SMarco Felsch usleep_range(1000, 1500); 165380a21da3SMarco Felsch 165480a21da3SMarco Felsch /* 165580a21da3SMarco Felsch * Enable the PLL here since it can be called by the clk-framework or by 165680a21da3SMarco Felsch * the .s_stream() callback. So this is the common place for both. 165780a21da3SMarco Felsch */ 165880a21da3SMarco Felsch err = tc358746_apply_pll_config(tc358746); 165980a21da3SMarco Felsch if (err) 166080a21da3SMarco Felsch goto err_clk; 166180a21da3SMarco Felsch 166280a21da3SMarco Felsch return 0; 166380a21da3SMarco Felsch 166480a21da3SMarco Felsch err_clk: 166580a21da3SMarco Felsch clk_disable_unprepare(tc358746->refclk); 166680a21da3SMarco Felsch err: 166780a21da3SMarco Felsch regulator_bulk_disable(ARRAY_SIZE(tc358746_supplies), 166880a21da3SMarco Felsch tc358746->supplies); 166980a21da3SMarco Felsch return err; 167080a21da3SMarco Felsch } 167180a21da3SMarco Felsch 16725edd1b4dSHans Verkuil static DEFINE_RUNTIME_DEV_PM_OPS(tc358746_pm_ops, tc358746_suspend, 167380a21da3SMarco Felsch tc358746_resume, NULL); 167480a21da3SMarco Felsch 167580a21da3SMarco Felsch static const struct of_device_id __maybe_unused tc358746_of_match[] = { 167680a21da3SMarco Felsch { .compatible = "toshiba,tc358746" }, 167780a21da3SMarco Felsch { }, 167880a21da3SMarco Felsch }; 167980a21da3SMarco Felsch MODULE_DEVICE_TABLE(of, tc358746_of_match); 168080a21da3SMarco Felsch 168180a21da3SMarco Felsch static struct i2c_driver tc358746_driver = { 168280a21da3SMarco Felsch .driver = { 168380a21da3SMarco Felsch .name = "tc358746", 168480a21da3SMarco Felsch .pm = pm_ptr(&tc358746_pm_ops), 168580a21da3SMarco Felsch .of_match_table = tc358746_of_match, 168680a21da3SMarco Felsch }, 168780a21da3SMarco Felsch .probe_new = tc358746_probe, 168880a21da3SMarco Felsch .remove = tc358746_remove, 168980a21da3SMarco Felsch }; 169080a21da3SMarco Felsch 169180a21da3SMarco Felsch module_i2c_driver(tc358746_driver); 169280a21da3SMarco Felsch 169380a21da3SMarco Felsch MODULE_DESCRIPTION("Toshiba TC358746 Parallel to CSI-2 bridge driver"); 169480a21da3SMarco Felsch MODULE_AUTHOR("Marco Felsch <kernel@pengutronix.de>"); 169580a21da3SMarco Felsch MODULE_LICENSE("GPL"); 1696