1*6f684d4fSMaxime Ripard // SPDX-License-Identifier: GPL-2.0+ 2*6f684d4fSMaxime Ripard /* 3*6f684d4fSMaxime Ripard * Driver for Cadence MIPI-CSI2 TX Controller 4*6f684d4fSMaxime Ripard * 5*6f684d4fSMaxime Ripard * Copyright (C) 2017-2018 Cadence Design Systems Inc. 6*6f684d4fSMaxime Ripard */ 7*6f684d4fSMaxime Ripard 8*6f684d4fSMaxime Ripard #include <linux/clk.h> 9*6f684d4fSMaxime Ripard #include <linux/delay.h> 10*6f684d4fSMaxime Ripard #include <linux/io.h> 11*6f684d4fSMaxime Ripard #include <linux/module.h> 12*6f684d4fSMaxime Ripard #include <linux/mutex.h> 13*6f684d4fSMaxime Ripard #include <linux/of.h> 14*6f684d4fSMaxime Ripard #include <linux/of_graph.h> 15*6f684d4fSMaxime Ripard #include <linux/platform_device.h> 16*6f684d4fSMaxime Ripard 17*6f684d4fSMaxime Ripard #include <media/v4l2-ctrls.h> 18*6f684d4fSMaxime Ripard #include <media/v4l2-device.h> 19*6f684d4fSMaxime Ripard #include <media/v4l2-fwnode.h> 20*6f684d4fSMaxime Ripard #include <media/v4l2-subdev.h> 21*6f684d4fSMaxime Ripard 22*6f684d4fSMaxime Ripard #define CSI2TX_DEVICE_CONFIG_REG 0x00 23*6f684d4fSMaxime Ripard #define CSI2TX_DEVICE_CONFIG_STREAMS_MASK GENMASK(6, 4) 24*6f684d4fSMaxime Ripard #define CSI2TX_DEVICE_CONFIG_HAS_DPHY BIT(3) 25*6f684d4fSMaxime Ripard #define CSI2TX_DEVICE_CONFIG_LANES_MASK GENMASK(2, 0) 26*6f684d4fSMaxime Ripard 27*6f684d4fSMaxime Ripard #define CSI2TX_CONFIG_REG 0x20 28*6f684d4fSMaxime Ripard #define CSI2TX_CONFIG_CFG_REQ BIT(2) 29*6f684d4fSMaxime Ripard #define CSI2TX_CONFIG_SRST_REQ BIT(1) 30*6f684d4fSMaxime Ripard 31*6f684d4fSMaxime Ripard #define CSI2TX_DPHY_CFG_REG 0x28 32*6f684d4fSMaxime Ripard #define CSI2TX_DPHY_CFG_CLK_RESET BIT(16) 33*6f684d4fSMaxime Ripard #define CSI2TX_DPHY_CFG_LANE_RESET(n) BIT((n) + 12) 34*6f684d4fSMaxime Ripard #define CSI2TX_DPHY_CFG_MODE_MASK GENMASK(9, 8) 35*6f684d4fSMaxime Ripard #define CSI2TX_DPHY_CFG_MODE_LPDT (2 << 8) 36*6f684d4fSMaxime Ripard #define CSI2TX_DPHY_CFG_MODE_HS (1 << 8) 37*6f684d4fSMaxime Ripard #define CSI2TX_DPHY_CFG_MODE_ULPS (0 << 8) 38*6f684d4fSMaxime Ripard #define CSI2TX_DPHY_CFG_CLK_ENABLE BIT(4) 39*6f684d4fSMaxime Ripard #define CSI2TX_DPHY_CFG_LANE_ENABLE(n) BIT(n) 40*6f684d4fSMaxime Ripard 41*6f684d4fSMaxime Ripard #define CSI2TX_DPHY_CLK_WAKEUP_REG 0x2c 42*6f684d4fSMaxime Ripard #define CSI2TX_DPHY_CLK_WAKEUP_ULPS_CYCLES(n) ((n) & 0xffff) 43*6f684d4fSMaxime Ripard 44*6f684d4fSMaxime Ripard #define CSI2TX_DT_CFG_REG(n) (0x80 + (n) * 8) 45*6f684d4fSMaxime Ripard #define CSI2TX_DT_CFG_DT(n) (((n) & 0x3f) << 2) 46*6f684d4fSMaxime Ripard 47*6f684d4fSMaxime Ripard #define CSI2TX_DT_FORMAT_REG(n) (0x84 + (n) * 8) 48*6f684d4fSMaxime Ripard #define CSI2TX_DT_FORMAT_BYTES_PER_LINE(n) (((n) & 0xffff) << 16) 49*6f684d4fSMaxime Ripard #define CSI2TX_DT_FORMAT_MAX_LINE_NUM(n) ((n) & 0xffff) 50*6f684d4fSMaxime Ripard 51*6f684d4fSMaxime Ripard #define CSI2TX_STREAM_IF_CFG_REG(n) (0x100 + (n) * 4) 52*6f684d4fSMaxime Ripard #define CSI2TX_STREAM_IF_CFG_FILL_LEVEL(n) ((n) & 0x1f) 53*6f684d4fSMaxime Ripard 54*6f684d4fSMaxime Ripard #define CSI2TX_LANES_MAX 4 55*6f684d4fSMaxime Ripard #define CSI2TX_STREAMS_MAX 4 56*6f684d4fSMaxime Ripard 57*6f684d4fSMaxime Ripard enum csi2tx_pads { 58*6f684d4fSMaxime Ripard CSI2TX_PAD_SOURCE, 59*6f684d4fSMaxime Ripard CSI2TX_PAD_SINK_STREAM0, 60*6f684d4fSMaxime Ripard CSI2TX_PAD_SINK_STREAM1, 61*6f684d4fSMaxime Ripard CSI2TX_PAD_SINK_STREAM2, 62*6f684d4fSMaxime Ripard CSI2TX_PAD_SINK_STREAM3, 63*6f684d4fSMaxime Ripard CSI2TX_PAD_MAX, 64*6f684d4fSMaxime Ripard }; 65*6f684d4fSMaxime Ripard 66*6f684d4fSMaxime Ripard struct csi2tx_fmt { 67*6f684d4fSMaxime Ripard u32 mbus; 68*6f684d4fSMaxime Ripard u32 dt; 69*6f684d4fSMaxime Ripard u32 bpp; 70*6f684d4fSMaxime Ripard }; 71*6f684d4fSMaxime Ripard 72*6f684d4fSMaxime Ripard struct csi2tx_priv { 73*6f684d4fSMaxime Ripard struct device *dev; 74*6f684d4fSMaxime Ripard unsigned int count; 75*6f684d4fSMaxime Ripard 76*6f684d4fSMaxime Ripard /* 77*6f684d4fSMaxime Ripard * Used to prevent race conditions between multiple, 78*6f684d4fSMaxime Ripard * concurrent calls to start and stop. 79*6f684d4fSMaxime Ripard */ 80*6f684d4fSMaxime Ripard struct mutex lock; 81*6f684d4fSMaxime Ripard 82*6f684d4fSMaxime Ripard void __iomem *base; 83*6f684d4fSMaxime Ripard 84*6f684d4fSMaxime Ripard struct clk *esc_clk; 85*6f684d4fSMaxime Ripard struct clk *p_clk; 86*6f684d4fSMaxime Ripard struct clk *pixel_clk[CSI2TX_STREAMS_MAX]; 87*6f684d4fSMaxime Ripard 88*6f684d4fSMaxime Ripard struct v4l2_subdev subdev; 89*6f684d4fSMaxime Ripard struct media_pad pads[CSI2TX_PAD_MAX]; 90*6f684d4fSMaxime Ripard struct v4l2_mbus_framefmt pad_fmts[CSI2TX_PAD_MAX]; 91*6f684d4fSMaxime Ripard 92*6f684d4fSMaxime Ripard bool has_internal_dphy; 93*6f684d4fSMaxime Ripard u8 lanes[CSI2TX_LANES_MAX]; 94*6f684d4fSMaxime Ripard unsigned int num_lanes; 95*6f684d4fSMaxime Ripard unsigned int max_lanes; 96*6f684d4fSMaxime Ripard unsigned int max_streams; 97*6f684d4fSMaxime Ripard }; 98*6f684d4fSMaxime Ripard 99*6f684d4fSMaxime Ripard static const struct csi2tx_fmt csi2tx_formats[] = { 100*6f684d4fSMaxime Ripard { 101*6f684d4fSMaxime Ripard .mbus = MEDIA_BUS_FMT_UYVY8_1X16, 102*6f684d4fSMaxime Ripard .bpp = 2, 103*6f684d4fSMaxime Ripard .dt = 0x1e, 104*6f684d4fSMaxime Ripard }, 105*6f684d4fSMaxime Ripard { 106*6f684d4fSMaxime Ripard .mbus = MEDIA_BUS_FMT_RGB888_1X24, 107*6f684d4fSMaxime Ripard .bpp = 3, 108*6f684d4fSMaxime Ripard .dt = 0x24, 109*6f684d4fSMaxime Ripard }, 110*6f684d4fSMaxime Ripard }; 111*6f684d4fSMaxime Ripard 112*6f684d4fSMaxime Ripard static const struct v4l2_mbus_framefmt fmt_default = { 113*6f684d4fSMaxime Ripard .width = 1280, 114*6f684d4fSMaxime Ripard .height = 720, 115*6f684d4fSMaxime Ripard .code = MEDIA_BUS_FMT_RGB888_1X24, 116*6f684d4fSMaxime Ripard .field = V4L2_FIELD_NONE, 117*6f684d4fSMaxime Ripard .colorspace = V4L2_COLORSPACE_DEFAULT, 118*6f684d4fSMaxime Ripard }; 119*6f684d4fSMaxime Ripard 120*6f684d4fSMaxime Ripard static inline 121*6f684d4fSMaxime Ripard struct csi2tx_priv *v4l2_subdev_to_csi2tx(struct v4l2_subdev *subdev) 122*6f684d4fSMaxime Ripard { 123*6f684d4fSMaxime Ripard return container_of(subdev, struct csi2tx_priv, subdev); 124*6f684d4fSMaxime Ripard } 125*6f684d4fSMaxime Ripard 126*6f684d4fSMaxime Ripard static const struct csi2tx_fmt *csi2tx_get_fmt_from_mbus(u32 mbus) 127*6f684d4fSMaxime Ripard { 128*6f684d4fSMaxime Ripard unsigned int i; 129*6f684d4fSMaxime Ripard 130*6f684d4fSMaxime Ripard for (i = 0; i < ARRAY_SIZE(csi2tx_formats); i++) 131*6f684d4fSMaxime Ripard if (csi2tx_formats[i].mbus == mbus) 132*6f684d4fSMaxime Ripard return &csi2tx_formats[i]; 133*6f684d4fSMaxime Ripard 134*6f684d4fSMaxime Ripard return NULL; 135*6f684d4fSMaxime Ripard } 136*6f684d4fSMaxime Ripard 137*6f684d4fSMaxime Ripard static int csi2tx_enum_mbus_code(struct v4l2_subdev *subdev, 138*6f684d4fSMaxime Ripard struct v4l2_subdev_pad_config *cfg, 139*6f684d4fSMaxime Ripard struct v4l2_subdev_mbus_code_enum *code) 140*6f684d4fSMaxime Ripard { 141*6f684d4fSMaxime Ripard if (code->pad || code->index >= ARRAY_SIZE(csi2tx_formats)) 142*6f684d4fSMaxime Ripard return -EINVAL; 143*6f684d4fSMaxime Ripard 144*6f684d4fSMaxime Ripard code->code = csi2tx_formats[code->index].mbus; 145*6f684d4fSMaxime Ripard 146*6f684d4fSMaxime Ripard return 0; 147*6f684d4fSMaxime Ripard } 148*6f684d4fSMaxime Ripard 149*6f684d4fSMaxime Ripard static struct v4l2_mbus_framefmt * 150*6f684d4fSMaxime Ripard __csi2tx_get_pad_format(struct v4l2_subdev *subdev, 151*6f684d4fSMaxime Ripard struct v4l2_subdev_pad_config *cfg, 152*6f684d4fSMaxime Ripard struct v4l2_subdev_format *fmt) 153*6f684d4fSMaxime Ripard { 154*6f684d4fSMaxime Ripard struct csi2tx_priv *csi2tx = v4l2_subdev_to_csi2tx(subdev); 155*6f684d4fSMaxime Ripard 156*6f684d4fSMaxime Ripard if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) 157*6f684d4fSMaxime Ripard return v4l2_subdev_get_try_format(subdev, cfg, 158*6f684d4fSMaxime Ripard fmt->pad); 159*6f684d4fSMaxime Ripard 160*6f684d4fSMaxime Ripard return &csi2tx->pad_fmts[fmt->pad]; 161*6f684d4fSMaxime Ripard } 162*6f684d4fSMaxime Ripard 163*6f684d4fSMaxime Ripard static int csi2tx_get_pad_format(struct v4l2_subdev *subdev, 164*6f684d4fSMaxime Ripard struct v4l2_subdev_pad_config *cfg, 165*6f684d4fSMaxime Ripard struct v4l2_subdev_format *fmt) 166*6f684d4fSMaxime Ripard { 167*6f684d4fSMaxime Ripard const struct v4l2_mbus_framefmt *format; 168*6f684d4fSMaxime Ripard 169*6f684d4fSMaxime Ripard /* Multiplexed pad? */ 170*6f684d4fSMaxime Ripard if (fmt->pad == CSI2TX_PAD_SOURCE) 171*6f684d4fSMaxime Ripard return -EINVAL; 172*6f684d4fSMaxime Ripard 173*6f684d4fSMaxime Ripard format = __csi2tx_get_pad_format(subdev, cfg, fmt); 174*6f684d4fSMaxime Ripard if (!format) 175*6f684d4fSMaxime Ripard return -EINVAL; 176*6f684d4fSMaxime Ripard 177*6f684d4fSMaxime Ripard fmt->format = *format; 178*6f684d4fSMaxime Ripard 179*6f684d4fSMaxime Ripard return 0; 180*6f684d4fSMaxime Ripard } 181*6f684d4fSMaxime Ripard 182*6f684d4fSMaxime Ripard static int csi2tx_set_pad_format(struct v4l2_subdev *subdev, 183*6f684d4fSMaxime Ripard struct v4l2_subdev_pad_config *cfg, 184*6f684d4fSMaxime Ripard struct v4l2_subdev_format *fmt) 185*6f684d4fSMaxime Ripard { 186*6f684d4fSMaxime Ripard const struct v4l2_mbus_framefmt *src_format = &fmt->format; 187*6f684d4fSMaxime Ripard struct v4l2_mbus_framefmt *dst_format; 188*6f684d4fSMaxime Ripard 189*6f684d4fSMaxime Ripard /* Multiplexed pad? */ 190*6f684d4fSMaxime Ripard if (fmt->pad == CSI2TX_PAD_SOURCE) 191*6f684d4fSMaxime Ripard return -EINVAL; 192*6f684d4fSMaxime Ripard 193*6f684d4fSMaxime Ripard if (!csi2tx_get_fmt_from_mbus(fmt->format.code)) 194*6f684d4fSMaxime Ripard src_format = &fmt_default; 195*6f684d4fSMaxime Ripard 196*6f684d4fSMaxime Ripard dst_format = __csi2tx_get_pad_format(subdev, cfg, fmt); 197*6f684d4fSMaxime Ripard if (!dst_format) 198*6f684d4fSMaxime Ripard return -EINVAL; 199*6f684d4fSMaxime Ripard 200*6f684d4fSMaxime Ripard *dst_format = *src_format; 201*6f684d4fSMaxime Ripard 202*6f684d4fSMaxime Ripard return 0; 203*6f684d4fSMaxime Ripard } 204*6f684d4fSMaxime Ripard 205*6f684d4fSMaxime Ripard static const struct v4l2_subdev_pad_ops csi2tx_pad_ops = { 206*6f684d4fSMaxime Ripard .enum_mbus_code = csi2tx_enum_mbus_code, 207*6f684d4fSMaxime Ripard .get_fmt = csi2tx_get_pad_format, 208*6f684d4fSMaxime Ripard .set_fmt = csi2tx_set_pad_format, 209*6f684d4fSMaxime Ripard }; 210*6f684d4fSMaxime Ripard 211*6f684d4fSMaxime Ripard static void csi2tx_reset(struct csi2tx_priv *csi2tx) 212*6f684d4fSMaxime Ripard { 213*6f684d4fSMaxime Ripard writel(CSI2TX_CONFIG_SRST_REQ, csi2tx->base + CSI2TX_CONFIG_REG); 214*6f684d4fSMaxime Ripard 215*6f684d4fSMaxime Ripard udelay(10); 216*6f684d4fSMaxime Ripard } 217*6f684d4fSMaxime Ripard 218*6f684d4fSMaxime Ripard static int csi2tx_start(struct csi2tx_priv *csi2tx) 219*6f684d4fSMaxime Ripard { 220*6f684d4fSMaxime Ripard struct media_entity *entity = &csi2tx->subdev.entity; 221*6f684d4fSMaxime Ripard struct media_link *link; 222*6f684d4fSMaxime Ripard unsigned int i; 223*6f684d4fSMaxime Ripard u32 reg; 224*6f684d4fSMaxime Ripard 225*6f684d4fSMaxime Ripard csi2tx_reset(csi2tx); 226*6f684d4fSMaxime Ripard 227*6f684d4fSMaxime Ripard writel(CSI2TX_CONFIG_CFG_REQ, csi2tx->base + CSI2TX_CONFIG_REG); 228*6f684d4fSMaxime Ripard 229*6f684d4fSMaxime Ripard udelay(10); 230*6f684d4fSMaxime Ripard 231*6f684d4fSMaxime Ripard /* Configure our PPI interface with the D-PHY */ 232*6f684d4fSMaxime Ripard writel(CSI2TX_DPHY_CLK_WAKEUP_ULPS_CYCLES(32), 233*6f684d4fSMaxime Ripard csi2tx->base + CSI2TX_DPHY_CLK_WAKEUP_REG); 234*6f684d4fSMaxime Ripard 235*6f684d4fSMaxime Ripard /* Put our lanes (clock and data) out of reset */ 236*6f684d4fSMaxime Ripard reg = CSI2TX_DPHY_CFG_CLK_RESET | CSI2TX_DPHY_CFG_MODE_LPDT; 237*6f684d4fSMaxime Ripard for (i = 0; i < csi2tx->num_lanes; i++) 238*6f684d4fSMaxime Ripard reg |= CSI2TX_DPHY_CFG_LANE_RESET(csi2tx->lanes[i]); 239*6f684d4fSMaxime Ripard writel(reg, csi2tx->base + CSI2TX_DPHY_CFG_REG); 240*6f684d4fSMaxime Ripard 241*6f684d4fSMaxime Ripard udelay(10); 242*6f684d4fSMaxime Ripard 243*6f684d4fSMaxime Ripard /* Enable our (clock and data) lanes */ 244*6f684d4fSMaxime Ripard reg |= CSI2TX_DPHY_CFG_CLK_ENABLE; 245*6f684d4fSMaxime Ripard for (i = 0; i < csi2tx->num_lanes; i++) 246*6f684d4fSMaxime Ripard reg |= CSI2TX_DPHY_CFG_LANE_ENABLE(csi2tx->lanes[i]); 247*6f684d4fSMaxime Ripard writel(reg, csi2tx->base + CSI2TX_DPHY_CFG_REG); 248*6f684d4fSMaxime Ripard 249*6f684d4fSMaxime Ripard udelay(10); 250*6f684d4fSMaxime Ripard 251*6f684d4fSMaxime Ripard /* Switch to HS mode */ 252*6f684d4fSMaxime Ripard reg &= ~CSI2TX_DPHY_CFG_MODE_MASK; 253*6f684d4fSMaxime Ripard writel(reg | CSI2TX_DPHY_CFG_MODE_HS, 254*6f684d4fSMaxime Ripard csi2tx->base + CSI2TX_DPHY_CFG_REG); 255*6f684d4fSMaxime Ripard 256*6f684d4fSMaxime Ripard udelay(10); 257*6f684d4fSMaxime Ripard 258*6f684d4fSMaxime Ripard /* 259*6f684d4fSMaxime Ripard * Create a static mapping between the CSI virtual channels 260*6f684d4fSMaxime Ripard * and the input streams. 261*6f684d4fSMaxime Ripard * 262*6f684d4fSMaxime Ripard * This should be enhanced, but v4l2 lacks the support for 263*6f684d4fSMaxime Ripard * changing that mapping dynamically at the moment. 264*6f684d4fSMaxime Ripard * 265*6f684d4fSMaxime Ripard * We're protected from the userspace setting up links at the 266*6f684d4fSMaxime Ripard * same time by the upper layer having called 267*6f684d4fSMaxime Ripard * media_pipeline_start(). 268*6f684d4fSMaxime Ripard */ 269*6f684d4fSMaxime Ripard list_for_each_entry(link, &entity->links, list) { 270*6f684d4fSMaxime Ripard struct v4l2_mbus_framefmt *mfmt; 271*6f684d4fSMaxime Ripard const struct csi2tx_fmt *fmt; 272*6f684d4fSMaxime Ripard unsigned int stream; 273*6f684d4fSMaxime Ripard int pad_idx = -1; 274*6f684d4fSMaxime Ripard 275*6f684d4fSMaxime Ripard /* Only consider our enabled input pads */ 276*6f684d4fSMaxime Ripard for (i = CSI2TX_PAD_SINK_STREAM0; i < CSI2TX_PAD_MAX; i++) { 277*6f684d4fSMaxime Ripard struct media_pad *pad = &csi2tx->pads[i]; 278*6f684d4fSMaxime Ripard 279*6f684d4fSMaxime Ripard if ((pad == link->sink) && 280*6f684d4fSMaxime Ripard (link->flags & MEDIA_LNK_FL_ENABLED)) { 281*6f684d4fSMaxime Ripard pad_idx = i; 282*6f684d4fSMaxime Ripard break; 283*6f684d4fSMaxime Ripard } 284*6f684d4fSMaxime Ripard } 285*6f684d4fSMaxime Ripard 286*6f684d4fSMaxime Ripard if (pad_idx < 0) 287*6f684d4fSMaxime Ripard continue; 288*6f684d4fSMaxime Ripard 289*6f684d4fSMaxime Ripard mfmt = &csi2tx->pad_fmts[pad_idx]; 290*6f684d4fSMaxime Ripard fmt = csi2tx_get_fmt_from_mbus(mfmt->code); 291*6f684d4fSMaxime Ripard if (!fmt) 292*6f684d4fSMaxime Ripard continue; 293*6f684d4fSMaxime Ripard 294*6f684d4fSMaxime Ripard stream = pad_idx - CSI2TX_PAD_SINK_STREAM0; 295*6f684d4fSMaxime Ripard 296*6f684d4fSMaxime Ripard /* 297*6f684d4fSMaxime Ripard * We use the stream ID there, but it's wrong. 298*6f684d4fSMaxime Ripard * 299*6f684d4fSMaxime Ripard * A stream could very well send a data type that is 300*6f684d4fSMaxime Ripard * not equal to its stream ID. We need to find a 301*6f684d4fSMaxime Ripard * proper way to address it. 302*6f684d4fSMaxime Ripard */ 303*6f684d4fSMaxime Ripard writel(CSI2TX_DT_CFG_DT(fmt->dt), 304*6f684d4fSMaxime Ripard csi2tx->base + CSI2TX_DT_CFG_REG(stream)); 305*6f684d4fSMaxime Ripard 306*6f684d4fSMaxime Ripard writel(CSI2TX_DT_FORMAT_BYTES_PER_LINE(mfmt->width * fmt->bpp) | 307*6f684d4fSMaxime Ripard CSI2TX_DT_FORMAT_MAX_LINE_NUM(mfmt->height + 1), 308*6f684d4fSMaxime Ripard csi2tx->base + CSI2TX_DT_FORMAT_REG(stream)); 309*6f684d4fSMaxime Ripard 310*6f684d4fSMaxime Ripard /* 311*6f684d4fSMaxime Ripard * TODO: This needs to be calculated based on the 312*6f684d4fSMaxime Ripard * output CSI2 clock rate. 313*6f684d4fSMaxime Ripard */ 314*6f684d4fSMaxime Ripard writel(CSI2TX_STREAM_IF_CFG_FILL_LEVEL(4), 315*6f684d4fSMaxime Ripard csi2tx->base + CSI2TX_STREAM_IF_CFG_REG(stream)); 316*6f684d4fSMaxime Ripard } 317*6f684d4fSMaxime Ripard 318*6f684d4fSMaxime Ripard /* Disable the configuration mode */ 319*6f684d4fSMaxime Ripard writel(0, csi2tx->base + CSI2TX_CONFIG_REG); 320*6f684d4fSMaxime Ripard 321*6f684d4fSMaxime Ripard return 0; 322*6f684d4fSMaxime Ripard } 323*6f684d4fSMaxime Ripard 324*6f684d4fSMaxime Ripard static void csi2tx_stop(struct csi2tx_priv *csi2tx) 325*6f684d4fSMaxime Ripard { 326*6f684d4fSMaxime Ripard writel(CSI2TX_CONFIG_CFG_REQ | CSI2TX_CONFIG_SRST_REQ, 327*6f684d4fSMaxime Ripard csi2tx->base + CSI2TX_CONFIG_REG); 328*6f684d4fSMaxime Ripard } 329*6f684d4fSMaxime Ripard 330*6f684d4fSMaxime Ripard static int csi2tx_s_stream(struct v4l2_subdev *subdev, int enable) 331*6f684d4fSMaxime Ripard { 332*6f684d4fSMaxime Ripard struct csi2tx_priv *csi2tx = v4l2_subdev_to_csi2tx(subdev); 333*6f684d4fSMaxime Ripard int ret = 0; 334*6f684d4fSMaxime Ripard 335*6f684d4fSMaxime Ripard mutex_lock(&csi2tx->lock); 336*6f684d4fSMaxime Ripard 337*6f684d4fSMaxime Ripard if (enable) { 338*6f684d4fSMaxime Ripard /* 339*6f684d4fSMaxime Ripard * If we're not the first users, there's no need to 340*6f684d4fSMaxime Ripard * enable the whole controller. 341*6f684d4fSMaxime Ripard */ 342*6f684d4fSMaxime Ripard if (!csi2tx->count) { 343*6f684d4fSMaxime Ripard ret = csi2tx_start(csi2tx); 344*6f684d4fSMaxime Ripard if (ret) 345*6f684d4fSMaxime Ripard goto out; 346*6f684d4fSMaxime Ripard } 347*6f684d4fSMaxime Ripard 348*6f684d4fSMaxime Ripard csi2tx->count++; 349*6f684d4fSMaxime Ripard } else { 350*6f684d4fSMaxime Ripard csi2tx->count--; 351*6f684d4fSMaxime Ripard 352*6f684d4fSMaxime Ripard /* 353*6f684d4fSMaxime Ripard * Let the last user turn off the lights. 354*6f684d4fSMaxime Ripard */ 355*6f684d4fSMaxime Ripard if (!csi2tx->count) 356*6f684d4fSMaxime Ripard csi2tx_stop(csi2tx); 357*6f684d4fSMaxime Ripard } 358*6f684d4fSMaxime Ripard 359*6f684d4fSMaxime Ripard out: 360*6f684d4fSMaxime Ripard mutex_unlock(&csi2tx->lock); 361*6f684d4fSMaxime Ripard return ret; 362*6f684d4fSMaxime Ripard } 363*6f684d4fSMaxime Ripard 364*6f684d4fSMaxime Ripard static const struct v4l2_subdev_video_ops csi2tx_video_ops = { 365*6f684d4fSMaxime Ripard .s_stream = csi2tx_s_stream, 366*6f684d4fSMaxime Ripard }; 367*6f684d4fSMaxime Ripard 368*6f684d4fSMaxime Ripard static const struct v4l2_subdev_ops csi2tx_subdev_ops = { 369*6f684d4fSMaxime Ripard .pad = &csi2tx_pad_ops, 370*6f684d4fSMaxime Ripard .video = &csi2tx_video_ops, 371*6f684d4fSMaxime Ripard }; 372*6f684d4fSMaxime Ripard 373*6f684d4fSMaxime Ripard static int csi2tx_get_resources(struct csi2tx_priv *csi2tx, 374*6f684d4fSMaxime Ripard struct platform_device *pdev) 375*6f684d4fSMaxime Ripard { 376*6f684d4fSMaxime Ripard struct resource *res; 377*6f684d4fSMaxime Ripard unsigned int i; 378*6f684d4fSMaxime Ripard u32 dev_cfg; 379*6f684d4fSMaxime Ripard 380*6f684d4fSMaxime Ripard res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 381*6f684d4fSMaxime Ripard csi2tx->base = devm_ioremap_resource(&pdev->dev, res); 382*6f684d4fSMaxime Ripard if (IS_ERR(csi2tx->base)) 383*6f684d4fSMaxime Ripard return PTR_ERR(csi2tx->base); 384*6f684d4fSMaxime Ripard 385*6f684d4fSMaxime Ripard csi2tx->p_clk = devm_clk_get(&pdev->dev, "p_clk"); 386*6f684d4fSMaxime Ripard if (IS_ERR(csi2tx->p_clk)) { 387*6f684d4fSMaxime Ripard dev_err(&pdev->dev, "Couldn't get p_clk\n"); 388*6f684d4fSMaxime Ripard return PTR_ERR(csi2tx->p_clk); 389*6f684d4fSMaxime Ripard } 390*6f684d4fSMaxime Ripard 391*6f684d4fSMaxime Ripard csi2tx->esc_clk = devm_clk_get(&pdev->dev, "esc_clk"); 392*6f684d4fSMaxime Ripard if (IS_ERR(csi2tx->esc_clk)) { 393*6f684d4fSMaxime Ripard dev_err(&pdev->dev, "Couldn't get the esc_clk\n"); 394*6f684d4fSMaxime Ripard return PTR_ERR(csi2tx->esc_clk); 395*6f684d4fSMaxime Ripard } 396*6f684d4fSMaxime Ripard 397*6f684d4fSMaxime Ripard clk_prepare_enable(csi2tx->p_clk); 398*6f684d4fSMaxime Ripard dev_cfg = readl(csi2tx->base + CSI2TX_DEVICE_CONFIG_REG); 399*6f684d4fSMaxime Ripard clk_disable_unprepare(csi2tx->p_clk); 400*6f684d4fSMaxime Ripard 401*6f684d4fSMaxime Ripard csi2tx->max_lanes = dev_cfg & CSI2TX_DEVICE_CONFIG_LANES_MASK; 402*6f684d4fSMaxime Ripard if (csi2tx->max_lanes > CSI2TX_LANES_MAX) { 403*6f684d4fSMaxime Ripard dev_err(&pdev->dev, "Invalid number of lanes: %u\n", 404*6f684d4fSMaxime Ripard csi2tx->max_lanes); 405*6f684d4fSMaxime Ripard return -EINVAL; 406*6f684d4fSMaxime Ripard } 407*6f684d4fSMaxime Ripard 408*6f684d4fSMaxime Ripard csi2tx->max_streams = (dev_cfg & CSI2TX_DEVICE_CONFIG_STREAMS_MASK) >> 4; 409*6f684d4fSMaxime Ripard if (csi2tx->max_streams > CSI2TX_STREAMS_MAX) { 410*6f684d4fSMaxime Ripard dev_err(&pdev->dev, "Invalid number of streams: %u\n", 411*6f684d4fSMaxime Ripard csi2tx->max_streams); 412*6f684d4fSMaxime Ripard return -EINVAL; 413*6f684d4fSMaxime Ripard } 414*6f684d4fSMaxime Ripard 415*6f684d4fSMaxime Ripard csi2tx->has_internal_dphy = !!(dev_cfg & CSI2TX_DEVICE_CONFIG_HAS_DPHY); 416*6f684d4fSMaxime Ripard 417*6f684d4fSMaxime Ripard for (i = 0; i < csi2tx->max_streams; i++) { 418*6f684d4fSMaxime Ripard char clk_name[16]; 419*6f684d4fSMaxime Ripard 420*6f684d4fSMaxime Ripard snprintf(clk_name, sizeof(clk_name), "pixel_if%u_clk", i); 421*6f684d4fSMaxime Ripard csi2tx->pixel_clk[i] = devm_clk_get(&pdev->dev, clk_name); 422*6f684d4fSMaxime Ripard if (IS_ERR(csi2tx->pixel_clk[i])) { 423*6f684d4fSMaxime Ripard dev_err(&pdev->dev, "Couldn't get clock %s\n", 424*6f684d4fSMaxime Ripard clk_name); 425*6f684d4fSMaxime Ripard return PTR_ERR(csi2tx->pixel_clk[i]); 426*6f684d4fSMaxime Ripard } 427*6f684d4fSMaxime Ripard } 428*6f684d4fSMaxime Ripard 429*6f684d4fSMaxime Ripard return 0; 430*6f684d4fSMaxime Ripard } 431*6f684d4fSMaxime Ripard 432*6f684d4fSMaxime Ripard static int csi2tx_check_lanes(struct csi2tx_priv *csi2tx) 433*6f684d4fSMaxime Ripard { 434*6f684d4fSMaxime Ripard struct v4l2_fwnode_endpoint v4l2_ep; 435*6f684d4fSMaxime Ripard struct device_node *ep; 436*6f684d4fSMaxime Ripard int ret; 437*6f684d4fSMaxime Ripard 438*6f684d4fSMaxime Ripard ep = of_graph_get_endpoint_by_regs(csi2tx->dev->of_node, 0, 0); 439*6f684d4fSMaxime Ripard if (!ep) 440*6f684d4fSMaxime Ripard return -EINVAL; 441*6f684d4fSMaxime Ripard 442*6f684d4fSMaxime Ripard ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(ep), &v4l2_ep); 443*6f684d4fSMaxime Ripard if (ret) { 444*6f684d4fSMaxime Ripard dev_err(csi2tx->dev, "Could not parse v4l2 endpoint\n"); 445*6f684d4fSMaxime Ripard goto out; 446*6f684d4fSMaxime Ripard } 447*6f684d4fSMaxime Ripard 448*6f684d4fSMaxime Ripard if (v4l2_ep.bus_type != V4L2_MBUS_CSI2) { 449*6f684d4fSMaxime Ripard dev_err(csi2tx->dev, "Unsupported media bus type: 0x%x\n", 450*6f684d4fSMaxime Ripard v4l2_ep.bus_type); 451*6f684d4fSMaxime Ripard ret = -EINVAL; 452*6f684d4fSMaxime Ripard goto out; 453*6f684d4fSMaxime Ripard } 454*6f684d4fSMaxime Ripard 455*6f684d4fSMaxime Ripard csi2tx->num_lanes = v4l2_ep.bus.mipi_csi2.num_data_lanes; 456*6f684d4fSMaxime Ripard if (csi2tx->num_lanes > csi2tx->max_lanes) { 457*6f684d4fSMaxime Ripard dev_err(csi2tx->dev, 458*6f684d4fSMaxime Ripard "Current configuration uses more lanes than supported\n"); 459*6f684d4fSMaxime Ripard ret = -EINVAL; 460*6f684d4fSMaxime Ripard goto out; 461*6f684d4fSMaxime Ripard } 462*6f684d4fSMaxime Ripard 463*6f684d4fSMaxime Ripard memcpy(csi2tx->lanes, v4l2_ep.bus.mipi_csi2.data_lanes, 464*6f684d4fSMaxime Ripard sizeof(csi2tx->lanes)); 465*6f684d4fSMaxime Ripard 466*6f684d4fSMaxime Ripard out: 467*6f684d4fSMaxime Ripard of_node_put(ep); 468*6f684d4fSMaxime Ripard return ret; 469*6f684d4fSMaxime Ripard } 470*6f684d4fSMaxime Ripard 471*6f684d4fSMaxime Ripard static int csi2tx_probe(struct platform_device *pdev) 472*6f684d4fSMaxime Ripard { 473*6f684d4fSMaxime Ripard struct csi2tx_priv *csi2tx; 474*6f684d4fSMaxime Ripard unsigned int i; 475*6f684d4fSMaxime Ripard int ret; 476*6f684d4fSMaxime Ripard 477*6f684d4fSMaxime Ripard csi2tx = kzalloc(sizeof(*csi2tx), GFP_KERNEL); 478*6f684d4fSMaxime Ripard if (!csi2tx) 479*6f684d4fSMaxime Ripard return -ENOMEM; 480*6f684d4fSMaxime Ripard platform_set_drvdata(pdev, csi2tx); 481*6f684d4fSMaxime Ripard mutex_init(&csi2tx->lock); 482*6f684d4fSMaxime Ripard csi2tx->dev = &pdev->dev; 483*6f684d4fSMaxime Ripard 484*6f684d4fSMaxime Ripard ret = csi2tx_get_resources(csi2tx, pdev); 485*6f684d4fSMaxime Ripard if (ret) 486*6f684d4fSMaxime Ripard goto err_free_priv; 487*6f684d4fSMaxime Ripard 488*6f684d4fSMaxime Ripard v4l2_subdev_init(&csi2tx->subdev, &csi2tx_subdev_ops); 489*6f684d4fSMaxime Ripard csi2tx->subdev.owner = THIS_MODULE; 490*6f684d4fSMaxime Ripard csi2tx->subdev.dev = &pdev->dev; 491*6f684d4fSMaxime Ripard csi2tx->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; 492*6f684d4fSMaxime Ripard snprintf(csi2tx->subdev.name, V4L2_SUBDEV_NAME_SIZE, "%s.%s", 493*6f684d4fSMaxime Ripard KBUILD_MODNAME, dev_name(&pdev->dev)); 494*6f684d4fSMaxime Ripard 495*6f684d4fSMaxime Ripard ret = csi2tx_check_lanes(csi2tx); 496*6f684d4fSMaxime Ripard if (ret) 497*6f684d4fSMaxime Ripard goto err_free_priv; 498*6f684d4fSMaxime Ripard 499*6f684d4fSMaxime Ripard /* Create our media pads */ 500*6f684d4fSMaxime Ripard csi2tx->subdev.entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; 501*6f684d4fSMaxime Ripard csi2tx->pads[CSI2TX_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; 502*6f684d4fSMaxime Ripard for (i = CSI2TX_PAD_SINK_STREAM0; i < CSI2TX_PAD_MAX; i++) 503*6f684d4fSMaxime Ripard csi2tx->pads[i].flags = MEDIA_PAD_FL_SINK; 504*6f684d4fSMaxime Ripard 505*6f684d4fSMaxime Ripard /* 506*6f684d4fSMaxime Ripard * Only the input pads are considered to have a format at the 507*6f684d4fSMaxime Ripard * moment. The CSI link can multiplex various streams with 508*6f684d4fSMaxime Ripard * different formats, and we can't expose this in v4l2 right 509*6f684d4fSMaxime Ripard * now. 510*6f684d4fSMaxime Ripard */ 511*6f684d4fSMaxime Ripard for (i = CSI2TX_PAD_SINK_STREAM0; i < CSI2TX_PAD_MAX; i++) 512*6f684d4fSMaxime Ripard csi2tx->pad_fmts[i] = fmt_default; 513*6f684d4fSMaxime Ripard 514*6f684d4fSMaxime Ripard ret = media_entity_pads_init(&csi2tx->subdev.entity, CSI2TX_PAD_MAX, 515*6f684d4fSMaxime Ripard csi2tx->pads); 516*6f684d4fSMaxime Ripard if (ret) 517*6f684d4fSMaxime Ripard goto err_free_priv; 518*6f684d4fSMaxime Ripard 519*6f684d4fSMaxime Ripard ret = v4l2_async_register_subdev(&csi2tx->subdev); 520*6f684d4fSMaxime Ripard if (ret < 0) 521*6f684d4fSMaxime Ripard goto err_free_priv; 522*6f684d4fSMaxime Ripard 523*6f684d4fSMaxime Ripard dev_info(&pdev->dev, 524*6f684d4fSMaxime Ripard "Probed CSI2TX with %u/%u lanes, %u streams, %s D-PHY\n", 525*6f684d4fSMaxime Ripard csi2tx->num_lanes, csi2tx->max_lanes, csi2tx->max_streams, 526*6f684d4fSMaxime Ripard csi2tx->has_internal_dphy ? "internal" : "no"); 527*6f684d4fSMaxime Ripard 528*6f684d4fSMaxime Ripard return 0; 529*6f684d4fSMaxime Ripard 530*6f684d4fSMaxime Ripard err_free_priv: 531*6f684d4fSMaxime Ripard kfree(csi2tx); 532*6f684d4fSMaxime Ripard return ret; 533*6f684d4fSMaxime Ripard } 534*6f684d4fSMaxime Ripard 535*6f684d4fSMaxime Ripard static int csi2tx_remove(struct platform_device *pdev) 536*6f684d4fSMaxime Ripard { 537*6f684d4fSMaxime Ripard struct csi2tx_priv *csi2tx = platform_get_drvdata(pdev); 538*6f684d4fSMaxime Ripard 539*6f684d4fSMaxime Ripard v4l2_async_unregister_subdev(&csi2tx->subdev); 540*6f684d4fSMaxime Ripard kfree(csi2tx); 541*6f684d4fSMaxime Ripard 542*6f684d4fSMaxime Ripard return 0; 543*6f684d4fSMaxime Ripard } 544*6f684d4fSMaxime Ripard 545*6f684d4fSMaxime Ripard static const struct of_device_id csi2tx_of_table[] = { 546*6f684d4fSMaxime Ripard { .compatible = "cdns,csi2tx" }, 547*6f684d4fSMaxime Ripard { }, 548*6f684d4fSMaxime Ripard }; 549*6f684d4fSMaxime Ripard MODULE_DEVICE_TABLE(of, csi2tx_of_table); 550*6f684d4fSMaxime Ripard 551*6f684d4fSMaxime Ripard static struct platform_driver csi2tx_driver = { 552*6f684d4fSMaxime Ripard .probe = csi2tx_probe, 553*6f684d4fSMaxime Ripard .remove = csi2tx_remove, 554*6f684d4fSMaxime Ripard 555*6f684d4fSMaxime Ripard .driver = { 556*6f684d4fSMaxime Ripard .name = "cdns-csi2tx", 557*6f684d4fSMaxime Ripard .of_match_table = csi2tx_of_table, 558*6f684d4fSMaxime Ripard }, 559*6f684d4fSMaxime Ripard }; 560*6f684d4fSMaxime Ripard module_platform_driver(csi2tx_driver); 561*6f684d4fSMaxime Ripard MODULE_AUTHOR("Maxime Ripard <maxime.ripard@bootlin.com>"); 562*6f684d4fSMaxime Ripard MODULE_DESCRIPTION("Cadence CSI2-TX controller"); 563*6f684d4fSMaxime Ripard MODULE_LICENSE("GPL"); 564