1*cf21f328SLaurent Pinchart // SPDX-License-Identifier: GPL-2.0 2*cf21f328SLaurent Pinchart /* 3*cf21f328SLaurent Pinchart * V4L2 Capture ISI subdev driver for i.MX8QXP/QM platform 4*cf21f328SLaurent Pinchart * 5*cf21f328SLaurent Pinchart * ISI is a Image Sensor Interface of i.MX8QXP/QM platform, which 6*cf21f328SLaurent Pinchart * used to process image from camera sensor to memory or DC 7*cf21f328SLaurent Pinchart * 8*cf21f328SLaurent Pinchart * Copyright (c) 2019 NXP Semiconductor 9*cf21f328SLaurent Pinchart */ 10*cf21f328SLaurent Pinchart 11*cf21f328SLaurent Pinchart #include <linux/device.h> 12*cf21f328SLaurent Pinchart #include <linux/errno.h> 13*cf21f328SLaurent Pinchart #include <linux/interrupt.h> 14*cf21f328SLaurent Pinchart #include <linux/kernel.h> 15*cf21f328SLaurent Pinchart #include <linux/minmax.h> 16*cf21f328SLaurent Pinchart #include <linux/mutex.h> 17*cf21f328SLaurent Pinchart #include <linux/of.h> 18*cf21f328SLaurent Pinchart #include <linux/platform_device.h> 19*cf21f328SLaurent Pinchart #include <linux/types.h> 20*cf21f328SLaurent Pinchart #include <linux/videodev2.h> 21*cf21f328SLaurent Pinchart 22*cf21f328SLaurent Pinchart #include <media/media-entity.h> 23*cf21f328SLaurent Pinchart #include <media/v4l2-subdev.h> 24*cf21f328SLaurent Pinchart #include <media/videobuf2-v4l2.h> 25*cf21f328SLaurent Pinchart 26*cf21f328SLaurent Pinchart #include "imx8-isi-core.h" 27*cf21f328SLaurent Pinchart #include "imx8-isi-regs.h" 28*cf21f328SLaurent Pinchart 29*cf21f328SLaurent Pinchart /* 30*cf21f328SLaurent Pinchart * While the ISI receives data from the gasket on a 3x12-bit bus, the pipeline 31*cf21f328SLaurent Pinchart * subdev conceptually includes the gasket in order to avoid exposing an extra 32*cf21f328SLaurent Pinchart * subdev between the CSIS and the ISI. We thus need to expose media bus codes 33*cf21f328SLaurent Pinchart * corresponding to the CSIS output, which is narrower. 34*cf21f328SLaurent Pinchart */ 35*cf21f328SLaurent Pinchart static const struct mxc_isi_bus_format_info mxc_isi_bus_formats[] = { 36*cf21f328SLaurent Pinchart /* YUV formats */ 37*cf21f328SLaurent Pinchart { 38*cf21f328SLaurent Pinchart .mbus_code = MEDIA_BUS_FMT_UYVY8_1X16, 39*cf21f328SLaurent Pinchart .output = MEDIA_BUS_FMT_YUV8_1X24, 40*cf21f328SLaurent Pinchart .pads = BIT(MXC_ISI_PIPE_PAD_SINK), 41*cf21f328SLaurent Pinchart .encoding = MXC_ISI_ENC_YUV, 42*cf21f328SLaurent Pinchart }, { 43*cf21f328SLaurent Pinchart .mbus_code = MEDIA_BUS_FMT_YUV8_1X24, 44*cf21f328SLaurent Pinchart .output = MEDIA_BUS_FMT_YUV8_1X24, 45*cf21f328SLaurent Pinchart .pads = BIT(MXC_ISI_PIPE_PAD_SOURCE), 46*cf21f328SLaurent Pinchart .encoding = MXC_ISI_ENC_YUV, 47*cf21f328SLaurent Pinchart }, 48*cf21f328SLaurent Pinchart /* RGB formats */ 49*cf21f328SLaurent Pinchart { 50*cf21f328SLaurent Pinchart .mbus_code = MEDIA_BUS_FMT_RGB565_1X16, 51*cf21f328SLaurent Pinchart .output = MEDIA_BUS_FMT_RGB888_1X24, 52*cf21f328SLaurent Pinchart .pads = BIT(MXC_ISI_PIPE_PAD_SINK), 53*cf21f328SLaurent Pinchart .encoding = MXC_ISI_ENC_RGB, 54*cf21f328SLaurent Pinchart }, { 55*cf21f328SLaurent Pinchart .mbus_code = MEDIA_BUS_FMT_RGB888_1X24, 56*cf21f328SLaurent Pinchart .output = MEDIA_BUS_FMT_RGB888_1X24, 57*cf21f328SLaurent Pinchart .pads = BIT(MXC_ISI_PIPE_PAD_SINK) 58*cf21f328SLaurent Pinchart | BIT(MXC_ISI_PIPE_PAD_SOURCE), 59*cf21f328SLaurent Pinchart .encoding = MXC_ISI_ENC_RGB, 60*cf21f328SLaurent Pinchart }, 61*cf21f328SLaurent Pinchart /* RAW formats */ 62*cf21f328SLaurent Pinchart { 63*cf21f328SLaurent Pinchart .mbus_code = MEDIA_BUS_FMT_Y8_1X8, 64*cf21f328SLaurent Pinchart .output = MEDIA_BUS_FMT_Y8_1X8, 65*cf21f328SLaurent Pinchart .pads = BIT(MXC_ISI_PIPE_PAD_SINK) 66*cf21f328SLaurent Pinchart | BIT(MXC_ISI_PIPE_PAD_SOURCE), 67*cf21f328SLaurent Pinchart .encoding = MXC_ISI_ENC_RAW, 68*cf21f328SLaurent Pinchart }, { 69*cf21f328SLaurent Pinchart .mbus_code = MEDIA_BUS_FMT_Y10_1X10, 70*cf21f328SLaurent Pinchart .output = MEDIA_BUS_FMT_Y10_1X10, 71*cf21f328SLaurent Pinchart .pads = BIT(MXC_ISI_PIPE_PAD_SINK) 72*cf21f328SLaurent Pinchart | BIT(MXC_ISI_PIPE_PAD_SOURCE), 73*cf21f328SLaurent Pinchart .encoding = MXC_ISI_ENC_RAW, 74*cf21f328SLaurent Pinchart }, { 75*cf21f328SLaurent Pinchart .mbus_code = MEDIA_BUS_FMT_Y12_1X12, 76*cf21f328SLaurent Pinchart .output = MEDIA_BUS_FMT_Y12_1X12, 77*cf21f328SLaurent Pinchart .pads = BIT(MXC_ISI_PIPE_PAD_SINK) 78*cf21f328SLaurent Pinchart | BIT(MXC_ISI_PIPE_PAD_SOURCE), 79*cf21f328SLaurent Pinchart .encoding = MXC_ISI_ENC_RAW, 80*cf21f328SLaurent Pinchart }, { 81*cf21f328SLaurent Pinchart .mbus_code = MEDIA_BUS_FMT_Y14_1X14, 82*cf21f328SLaurent Pinchart .output = MEDIA_BUS_FMT_Y14_1X14, 83*cf21f328SLaurent Pinchart .pads = BIT(MXC_ISI_PIPE_PAD_SINK) 84*cf21f328SLaurent Pinchart | BIT(MXC_ISI_PIPE_PAD_SOURCE), 85*cf21f328SLaurent Pinchart .encoding = MXC_ISI_ENC_RAW, 86*cf21f328SLaurent Pinchart }, { 87*cf21f328SLaurent Pinchart .mbus_code = MEDIA_BUS_FMT_SBGGR8_1X8, 88*cf21f328SLaurent Pinchart .output = MEDIA_BUS_FMT_SBGGR8_1X8, 89*cf21f328SLaurent Pinchart .pads = BIT(MXC_ISI_PIPE_PAD_SINK) 90*cf21f328SLaurent Pinchart | BIT(MXC_ISI_PIPE_PAD_SOURCE), 91*cf21f328SLaurent Pinchart .encoding = MXC_ISI_ENC_RAW, 92*cf21f328SLaurent Pinchart }, { 93*cf21f328SLaurent Pinchart .mbus_code = MEDIA_BUS_FMT_SGBRG8_1X8, 94*cf21f328SLaurent Pinchart .output = MEDIA_BUS_FMT_SGBRG8_1X8, 95*cf21f328SLaurent Pinchart .pads = BIT(MXC_ISI_PIPE_PAD_SINK) 96*cf21f328SLaurent Pinchart | BIT(MXC_ISI_PIPE_PAD_SOURCE), 97*cf21f328SLaurent Pinchart .encoding = MXC_ISI_ENC_RAW, 98*cf21f328SLaurent Pinchart }, { 99*cf21f328SLaurent Pinchart .mbus_code = MEDIA_BUS_FMT_SGRBG8_1X8, 100*cf21f328SLaurent Pinchart .output = MEDIA_BUS_FMT_SGRBG8_1X8, 101*cf21f328SLaurent Pinchart .pads = BIT(MXC_ISI_PIPE_PAD_SINK) 102*cf21f328SLaurent Pinchart | BIT(MXC_ISI_PIPE_PAD_SOURCE), 103*cf21f328SLaurent Pinchart .encoding = MXC_ISI_ENC_RAW, 104*cf21f328SLaurent Pinchart }, { 105*cf21f328SLaurent Pinchart .mbus_code = MEDIA_BUS_FMT_SRGGB8_1X8, 106*cf21f328SLaurent Pinchart .output = MEDIA_BUS_FMT_SRGGB8_1X8, 107*cf21f328SLaurent Pinchart .pads = BIT(MXC_ISI_PIPE_PAD_SINK) 108*cf21f328SLaurent Pinchart | BIT(MXC_ISI_PIPE_PAD_SOURCE), 109*cf21f328SLaurent Pinchart .encoding = MXC_ISI_ENC_RAW, 110*cf21f328SLaurent Pinchart }, { 111*cf21f328SLaurent Pinchart .mbus_code = MEDIA_BUS_FMT_SBGGR10_1X10, 112*cf21f328SLaurent Pinchart .output = MEDIA_BUS_FMT_SBGGR10_1X10, 113*cf21f328SLaurent Pinchart .pads = BIT(MXC_ISI_PIPE_PAD_SINK) 114*cf21f328SLaurent Pinchart | BIT(MXC_ISI_PIPE_PAD_SOURCE), 115*cf21f328SLaurent Pinchart .encoding = MXC_ISI_ENC_RAW, 116*cf21f328SLaurent Pinchart }, { 117*cf21f328SLaurent Pinchart .mbus_code = MEDIA_BUS_FMT_SGBRG10_1X10, 118*cf21f328SLaurent Pinchart .output = MEDIA_BUS_FMT_SGBRG10_1X10, 119*cf21f328SLaurent Pinchart .pads = BIT(MXC_ISI_PIPE_PAD_SINK) 120*cf21f328SLaurent Pinchart | BIT(MXC_ISI_PIPE_PAD_SOURCE), 121*cf21f328SLaurent Pinchart .encoding = MXC_ISI_ENC_RAW, 122*cf21f328SLaurent Pinchart }, { 123*cf21f328SLaurent Pinchart .mbus_code = MEDIA_BUS_FMT_SGRBG10_1X10, 124*cf21f328SLaurent Pinchart .output = MEDIA_BUS_FMT_SGRBG10_1X10, 125*cf21f328SLaurent Pinchart .pads = BIT(MXC_ISI_PIPE_PAD_SINK) 126*cf21f328SLaurent Pinchart | BIT(MXC_ISI_PIPE_PAD_SOURCE), 127*cf21f328SLaurent Pinchart .encoding = MXC_ISI_ENC_RAW, 128*cf21f328SLaurent Pinchart }, { 129*cf21f328SLaurent Pinchart .mbus_code = MEDIA_BUS_FMT_SRGGB10_1X10, 130*cf21f328SLaurent Pinchart .output = MEDIA_BUS_FMT_SRGGB10_1X10, 131*cf21f328SLaurent Pinchart .pads = BIT(MXC_ISI_PIPE_PAD_SINK) 132*cf21f328SLaurent Pinchart | BIT(MXC_ISI_PIPE_PAD_SOURCE), 133*cf21f328SLaurent Pinchart .encoding = MXC_ISI_ENC_RAW, 134*cf21f328SLaurent Pinchart }, { 135*cf21f328SLaurent Pinchart .mbus_code = MEDIA_BUS_FMT_SBGGR12_1X12, 136*cf21f328SLaurent Pinchart .output = MEDIA_BUS_FMT_SBGGR12_1X12, 137*cf21f328SLaurent Pinchart .pads = BIT(MXC_ISI_PIPE_PAD_SINK) 138*cf21f328SLaurent Pinchart | BIT(MXC_ISI_PIPE_PAD_SOURCE), 139*cf21f328SLaurent Pinchart .encoding = MXC_ISI_ENC_RAW, 140*cf21f328SLaurent Pinchart }, { 141*cf21f328SLaurent Pinchart .mbus_code = MEDIA_BUS_FMT_SGBRG12_1X12, 142*cf21f328SLaurent Pinchart .output = MEDIA_BUS_FMT_SGBRG12_1X12, 143*cf21f328SLaurent Pinchart .pads = BIT(MXC_ISI_PIPE_PAD_SINK) 144*cf21f328SLaurent Pinchart | BIT(MXC_ISI_PIPE_PAD_SOURCE), 145*cf21f328SLaurent Pinchart .encoding = MXC_ISI_ENC_RAW, 146*cf21f328SLaurent Pinchart }, { 147*cf21f328SLaurent Pinchart .mbus_code = MEDIA_BUS_FMT_SGRBG12_1X12, 148*cf21f328SLaurent Pinchart .output = MEDIA_BUS_FMT_SGRBG12_1X12, 149*cf21f328SLaurent Pinchart .pads = BIT(MXC_ISI_PIPE_PAD_SINK) 150*cf21f328SLaurent Pinchart | BIT(MXC_ISI_PIPE_PAD_SOURCE), 151*cf21f328SLaurent Pinchart .encoding = MXC_ISI_ENC_RAW, 152*cf21f328SLaurent Pinchart }, { 153*cf21f328SLaurent Pinchart .mbus_code = MEDIA_BUS_FMT_SRGGB12_1X12, 154*cf21f328SLaurent Pinchart .output = MEDIA_BUS_FMT_SRGGB12_1X12, 155*cf21f328SLaurent Pinchart .pads = BIT(MXC_ISI_PIPE_PAD_SINK) 156*cf21f328SLaurent Pinchart | BIT(MXC_ISI_PIPE_PAD_SOURCE), 157*cf21f328SLaurent Pinchart .encoding = MXC_ISI_ENC_RAW, 158*cf21f328SLaurent Pinchart }, { 159*cf21f328SLaurent Pinchart .mbus_code = MEDIA_BUS_FMT_SBGGR14_1X14, 160*cf21f328SLaurent Pinchart .output = MEDIA_BUS_FMT_SBGGR14_1X14, 161*cf21f328SLaurent Pinchart .pads = BIT(MXC_ISI_PIPE_PAD_SINK) 162*cf21f328SLaurent Pinchart | BIT(MXC_ISI_PIPE_PAD_SOURCE), 163*cf21f328SLaurent Pinchart .encoding = MXC_ISI_ENC_RAW, 164*cf21f328SLaurent Pinchart }, { 165*cf21f328SLaurent Pinchart .mbus_code = MEDIA_BUS_FMT_SGBRG14_1X14, 166*cf21f328SLaurent Pinchart .output = MEDIA_BUS_FMT_SGBRG14_1X14, 167*cf21f328SLaurent Pinchart .pads = BIT(MXC_ISI_PIPE_PAD_SINK) 168*cf21f328SLaurent Pinchart | BIT(MXC_ISI_PIPE_PAD_SOURCE), 169*cf21f328SLaurent Pinchart .encoding = MXC_ISI_ENC_RAW, 170*cf21f328SLaurent Pinchart }, { 171*cf21f328SLaurent Pinchart .mbus_code = MEDIA_BUS_FMT_SGRBG14_1X14, 172*cf21f328SLaurent Pinchart .output = MEDIA_BUS_FMT_SGRBG14_1X14, 173*cf21f328SLaurent Pinchart .pads = BIT(MXC_ISI_PIPE_PAD_SINK) 174*cf21f328SLaurent Pinchart | BIT(MXC_ISI_PIPE_PAD_SOURCE), 175*cf21f328SLaurent Pinchart .encoding = MXC_ISI_ENC_RAW, 176*cf21f328SLaurent Pinchart }, { 177*cf21f328SLaurent Pinchart .mbus_code = MEDIA_BUS_FMT_SRGGB14_1X14, 178*cf21f328SLaurent Pinchart .output = MEDIA_BUS_FMT_SRGGB14_1X14, 179*cf21f328SLaurent Pinchart .pads = BIT(MXC_ISI_PIPE_PAD_SINK) 180*cf21f328SLaurent Pinchart | BIT(MXC_ISI_PIPE_PAD_SOURCE), 181*cf21f328SLaurent Pinchart .encoding = MXC_ISI_ENC_RAW, 182*cf21f328SLaurent Pinchart }, 183*cf21f328SLaurent Pinchart /* JPEG */ 184*cf21f328SLaurent Pinchart { 185*cf21f328SLaurent Pinchart .mbus_code = MEDIA_BUS_FMT_JPEG_1X8, 186*cf21f328SLaurent Pinchart .output = MEDIA_BUS_FMT_JPEG_1X8, 187*cf21f328SLaurent Pinchart .pads = BIT(MXC_ISI_PIPE_PAD_SINK) 188*cf21f328SLaurent Pinchart | BIT(MXC_ISI_PIPE_PAD_SOURCE), 189*cf21f328SLaurent Pinchart .encoding = MXC_ISI_ENC_RAW, 190*cf21f328SLaurent Pinchart } 191*cf21f328SLaurent Pinchart }; 192*cf21f328SLaurent Pinchart 193*cf21f328SLaurent Pinchart const struct mxc_isi_bus_format_info * 194*cf21f328SLaurent Pinchart mxc_isi_bus_format_by_code(u32 code, unsigned int pad) 195*cf21f328SLaurent Pinchart { 196*cf21f328SLaurent Pinchart unsigned int i; 197*cf21f328SLaurent Pinchart 198*cf21f328SLaurent Pinchart for (i = 0; i < ARRAY_SIZE(mxc_isi_bus_formats); i++) { 199*cf21f328SLaurent Pinchart const struct mxc_isi_bus_format_info *info = 200*cf21f328SLaurent Pinchart &mxc_isi_bus_formats[i]; 201*cf21f328SLaurent Pinchart 202*cf21f328SLaurent Pinchart if (info->mbus_code == code && info->pads & BIT(pad)) 203*cf21f328SLaurent Pinchart return info; 204*cf21f328SLaurent Pinchart } 205*cf21f328SLaurent Pinchart 206*cf21f328SLaurent Pinchart return NULL; 207*cf21f328SLaurent Pinchart } 208*cf21f328SLaurent Pinchart 209*cf21f328SLaurent Pinchart const struct mxc_isi_bus_format_info * 210*cf21f328SLaurent Pinchart mxc_isi_bus_format_by_index(unsigned int index, unsigned int pad) 211*cf21f328SLaurent Pinchart { 212*cf21f328SLaurent Pinchart unsigned int i; 213*cf21f328SLaurent Pinchart 214*cf21f328SLaurent Pinchart for (i = 0; i < ARRAY_SIZE(mxc_isi_bus_formats); i++) { 215*cf21f328SLaurent Pinchart const struct mxc_isi_bus_format_info *info = 216*cf21f328SLaurent Pinchart &mxc_isi_bus_formats[i]; 217*cf21f328SLaurent Pinchart 218*cf21f328SLaurent Pinchart if (!(info->pads & BIT(pad))) 219*cf21f328SLaurent Pinchart continue; 220*cf21f328SLaurent Pinchart 221*cf21f328SLaurent Pinchart if (!index) 222*cf21f328SLaurent Pinchart return info; 223*cf21f328SLaurent Pinchart 224*cf21f328SLaurent Pinchart index--; 225*cf21f328SLaurent Pinchart } 226*cf21f328SLaurent Pinchart 227*cf21f328SLaurent Pinchart return NULL; 228*cf21f328SLaurent Pinchart } 229*cf21f328SLaurent Pinchart 230*cf21f328SLaurent Pinchart static inline struct mxc_isi_pipe *to_isi_pipe(struct v4l2_subdev *sd) 231*cf21f328SLaurent Pinchart { 232*cf21f328SLaurent Pinchart return container_of(sd, struct mxc_isi_pipe, sd); 233*cf21f328SLaurent Pinchart } 234*cf21f328SLaurent Pinchart 235*cf21f328SLaurent Pinchart int mxc_isi_pipe_enable(struct mxc_isi_pipe *pipe) 236*cf21f328SLaurent Pinchart { 237*cf21f328SLaurent Pinchart struct mxc_isi_crossbar *xbar = &pipe->isi->crossbar; 238*cf21f328SLaurent Pinchart const struct mxc_isi_bus_format_info *sink_info; 239*cf21f328SLaurent Pinchart const struct mxc_isi_bus_format_info *src_info; 240*cf21f328SLaurent Pinchart const struct v4l2_mbus_framefmt *sink_fmt; 241*cf21f328SLaurent Pinchart const struct v4l2_mbus_framefmt *src_fmt; 242*cf21f328SLaurent Pinchart const struct v4l2_rect *compose; 243*cf21f328SLaurent Pinchart struct v4l2_subdev_state *state; 244*cf21f328SLaurent Pinchart struct v4l2_subdev *sd = &pipe->sd; 245*cf21f328SLaurent Pinchart struct v4l2_area in_size, scale; 246*cf21f328SLaurent Pinchart struct v4l2_rect crop; 247*cf21f328SLaurent Pinchart u32 input; 248*cf21f328SLaurent Pinchart int ret; 249*cf21f328SLaurent Pinchart 250*cf21f328SLaurent Pinchart /* 251*cf21f328SLaurent Pinchart * Find the connected input by inspecting the crossbar switch routing 252*cf21f328SLaurent Pinchart * table. 253*cf21f328SLaurent Pinchart */ 254*cf21f328SLaurent Pinchart state = v4l2_subdev_lock_and_get_active_state(&xbar->sd); 255*cf21f328SLaurent Pinchart ret = v4l2_subdev_routing_find_opposite_end(&state->routing, 256*cf21f328SLaurent Pinchart xbar->num_sinks + pipe->id, 257*cf21f328SLaurent Pinchart 0, &input, NULL); 258*cf21f328SLaurent Pinchart v4l2_subdev_unlock_state(state); 259*cf21f328SLaurent Pinchart 260*cf21f328SLaurent Pinchart if (ret) 261*cf21f328SLaurent Pinchart return -EPIPE; 262*cf21f328SLaurent Pinchart 263*cf21f328SLaurent Pinchart /* Configure the pipeline. */ 264*cf21f328SLaurent Pinchart state = v4l2_subdev_lock_and_get_active_state(sd); 265*cf21f328SLaurent Pinchart 266*cf21f328SLaurent Pinchart sink_fmt = v4l2_subdev_get_try_format(sd, state, MXC_ISI_PIPE_PAD_SINK); 267*cf21f328SLaurent Pinchart src_fmt = v4l2_subdev_get_try_format(sd, state, MXC_ISI_PIPE_PAD_SOURCE); 268*cf21f328SLaurent Pinchart compose = v4l2_subdev_get_try_compose(sd, state, MXC_ISI_PIPE_PAD_SINK); 269*cf21f328SLaurent Pinchart crop = *v4l2_subdev_get_try_crop(sd, state, MXC_ISI_PIPE_PAD_SOURCE); 270*cf21f328SLaurent Pinchart 271*cf21f328SLaurent Pinchart sink_info = mxc_isi_bus_format_by_code(sink_fmt->code, 272*cf21f328SLaurent Pinchart MXC_ISI_PIPE_PAD_SINK); 273*cf21f328SLaurent Pinchart src_info = mxc_isi_bus_format_by_code(src_fmt->code, 274*cf21f328SLaurent Pinchart MXC_ISI_PIPE_PAD_SOURCE); 275*cf21f328SLaurent Pinchart 276*cf21f328SLaurent Pinchart in_size.width = sink_fmt->width; 277*cf21f328SLaurent Pinchart in_size.height = sink_fmt->height; 278*cf21f328SLaurent Pinchart scale.width = compose->width; 279*cf21f328SLaurent Pinchart scale.height = compose->height; 280*cf21f328SLaurent Pinchart 281*cf21f328SLaurent Pinchart v4l2_subdev_unlock_state(state); 282*cf21f328SLaurent Pinchart 283*cf21f328SLaurent Pinchart /* Configure the ISI channel. */ 284*cf21f328SLaurent Pinchart mxc_isi_channel_config(pipe, input, &in_size, &scale, &crop, 285*cf21f328SLaurent Pinchart sink_info->encoding, src_info->encoding); 286*cf21f328SLaurent Pinchart 287*cf21f328SLaurent Pinchart mxc_isi_channel_enable(pipe); 288*cf21f328SLaurent Pinchart 289*cf21f328SLaurent Pinchart /* Enable streams on the crossbar switch. */ 290*cf21f328SLaurent Pinchart ret = v4l2_subdev_enable_streams(&xbar->sd, xbar->num_sinks + pipe->id, 291*cf21f328SLaurent Pinchart BIT(0)); 292*cf21f328SLaurent Pinchart if (ret) { 293*cf21f328SLaurent Pinchart mxc_isi_channel_disable(pipe); 294*cf21f328SLaurent Pinchart dev_err(pipe->isi->dev, "Failed to enable pipe %u\n", 295*cf21f328SLaurent Pinchart pipe->id); 296*cf21f328SLaurent Pinchart return ret; 297*cf21f328SLaurent Pinchart } 298*cf21f328SLaurent Pinchart 299*cf21f328SLaurent Pinchart return 0; 300*cf21f328SLaurent Pinchart } 301*cf21f328SLaurent Pinchart 302*cf21f328SLaurent Pinchart void mxc_isi_pipe_disable(struct mxc_isi_pipe *pipe) 303*cf21f328SLaurent Pinchart { 304*cf21f328SLaurent Pinchart struct mxc_isi_crossbar *xbar = &pipe->isi->crossbar; 305*cf21f328SLaurent Pinchart int ret; 306*cf21f328SLaurent Pinchart 307*cf21f328SLaurent Pinchart ret = v4l2_subdev_disable_streams(&xbar->sd, xbar->num_sinks + pipe->id, 308*cf21f328SLaurent Pinchart BIT(0)); 309*cf21f328SLaurent Pinchart if (ret) 310*cf21f328SLaurent Pinchart dev_err(pipe->isi->dev, "Failed to disable pipe %u\n", 311*cf21f328SLaurent Pinchart pipe->id); 312*cf21f328SLaurent Pinchart 313*cf21f328SLaurent Pinchart mxc_isi_channel_disable(pipe); 314*cf21f328SLaurent Pinchart } 315*cf21f328SLaurent Pinchart 316*cf21f328SLaurent Pinchart /* ----------------------------------------------------------------------------- 317*cf21f328SLaurent Pinchart * V4L2 subdev operations 318*cf21f328SLaurent Pinchart */ 319*cf21f328SLaurent Pinchart 320*cf21f328SLaurent Pinchart static struct v4l2_mbus_framefmt * 321*cf21f328SLaurent Pinchart mxc_isi_pipe_get_pad_format(struct mxc_isi_pipe *pipe, 322*cf21f328SLaurent Pinchart struct v4l2_subdev_state *state, 323*cf21f328SLaurent Pinchart unsigned int pad) 324*cf21f328SLaurent Pinchart { 325*cf21f328SLaurent Pinchart return v4l2_subdev_get_try_format(&pipe->sd, state, pad); 326*cf21f328SLaurent Pinchart } 327*cf21f328SLaurent Pinchart 328*cf21f328SLaurent Pinchart static struct v4l2_rect * 329*cf21f328SLaurent Pinchart mxc_isi_pipe_get_pad_crop(struct mxc_isi_pipe *pipe, 330*cf21f328SLaurent Pinchart struct v4l2_subdev_state *state, 331*cf21f328SLaurent Pinchart unsigned int pad) 332*cf21f328SLaurent Pinchart { 333*cf21f328SLaurent Pinchart return v4l2_subdev_get_try_crop(&pipe->sd, state, pad); 334*cf21f328SLaurent Pinchart } 335*cf21f328SLaurent Pinchart 336*cf21f328SLaurent Pinchart static struct v4l2_rect * 337*cf21f328SLaurent Pinchart mxc_isi_pipe_get_pad_compose(struct mxc_isi_pipe *pipe, 338*cf21f328SLaurent Pinchart struct v4l2_subdev_state *state, 339*cf21f328SLaurent Pinchart unsigned int pad) 340*cf21f328SLaurent Pinchart { 341*cf21f328SLaurent Pinchart return v4l2_subdev_get_try_compose(&pipe->sd, state, pad); 342*cf21f328SLaurent Pinchart } 343*cf21f328SLaurent Pinchart 344*cf21f328SLaurent Pinchart static int mxc_isi_pipe_init_cfg(struct v4l2_subdev *sd, 345*cf21f328SLaurent Pinchart struct v4l2_subdev_state *state) 346*cf21f328SLaurent Pinchart { 347*cf21f328SLaurent Pinchart struct mxc_isi_pipe *pipe = to_isi_pipe(sd); 348*cf21f328SLaurent Pinchart struct v4l2_mbus_framefmt *fmt_source; 349*cf21f328SLaurent Pinchart struct v4l2_mbus_framefmt *fmt_sink; 350*cf21f328SLaurent Pinchart struct v4l2_rect *compose; 351*cf21f328SLaurent Pinchart struct v4l2_rect *crop; 352*cf21f328SLaurent Pinchart 353*cf21f328SLaurent Pinchart fmt_sink = mxc_isi_pipe_get_pad_format(pipe, state, 354*cf21f328SLaurent Pinchart MXC_ISI_PIPE_PAD_SINK); 355*cf21f328SLaurent Pinchart fmt_source = mxc_isi_pipe_get_pad_format(pipe, state, 356*cf21f328SLaurent Pinchart MXC_ISI_PIPE_PAD_SOURCE); 357*cf21f328SLaurent Pinchart 358*cf21f328SLaurent Pinchart fmt_sink->width = MXC_ISI_DEF_WIDTH; 359*cf21f328SLaurent Pinchart fmt_sink->height = MXC_ISI_DEF_HEIGHT; 360*cf21f328SLaurent Pinchart fmt_sink->code = MXC_ISI_DEF_MBUS_CODE_SINK; 361*cf21f328SLaurent Pinchart fmt_sink->field = V4L2_FIELD_NONE; 362*cf21f328SLaurent Pinchart fmt_sink->colorspace = V4L2_COLORSPACE_JPEG; 363*cf21f328SLaurent Pinchart fmt_sink->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt_sink->colorspace); 364*cf21f328SLaurent Pinchart fmt_sink->quantization = 365*cf21f328SLaurent Pinchart V4L2_MAP_QUANTIZATION_DEFAULT(false, fmt_sink->colorspace, 366*cf21f328SLaurent Pinchart fmt_sink->ycbcr_enc); 367*cf21f328SLaurent Pinchart fmt_sink->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(fmt_sink->colorspace); 368*cf21f328SLaurent Pinchart 369*cf21f328SLaurent Pinchart *fmt_source = *fmt_sink; 370*cf21f328SLaurent Pinchart fmt_source->code = MXC_ISI_DEF_MBUS_CODE_SOURCE; 371*cf21f328SLaurent Pinchart 372*cf21f328SLaurent Pinchart compose = mxc_isi_pipe_get_pad_compose(pipe, state, 373*cf21f328SLaurent Pinchart MXC_ISI_PIPE_PAD_SINK); 374*cf21f328SLaurent Pinchart crop = mxc_isi_pipe_get_pad_crop(pipe, state, MXC_ISI_PIPE_PAD_SOURCE); 375*cf21f328SLaurent Pinchart 376*cf21f328SLaurent Pinchart compose->left = 0; 377*cf21f328SLaurent Pinchart compose->top = 0; 378*cf21f328SLaurent Pinchart compose->width = MXC_ISI_DEF_WIDTH; 379*cf21f328SLaurent Pinchart compose->height = MXC_ISI_DEF_HEIGHT; 380*cf21f328SLaurent Pinchart 381*cf21f328SLaurent Pinchart *crop = *compose; 382*cf21f328SLaurent Pinchart 383*cf21f328SLaurent Pinchart return 0; 384*cf21f328SLaurent Pinchart } 385*cf21f328SLaurent Pinchart 386*cf21f328SLaurent Pinchart static int mxc_isi_pipe_enum_mbus_code(struct v4l2_subdev *sd, 387*cf21f328SLaurent Pinchart struct v4l2_subdev_state *state, 388*cf21f328SLaurent Pinchart struct v4l2_subdev_mbus_code_enum *code) 389*cf21f328SLaurent Pinchart { 390*cf21f328SLaurent Pinchart static const u32 output_codes[] = { 391*cf21f328SLaurent Pinchart MEDIA_BUS_FMT_YUV8_1X24, 392*cf21f328SLaurent Pinchart MEDIA_BUS_FMT_RGB888_1X24, 393*cf21f328SLaurent Pinchart }; 394*cf21f328SLaurent Pinchart struct mxc_isi_pipe *pipe = to_isi_pipe(sd); 395*cf21f328SLaurent Pinchart const struct mxc_isi_bus_format_info *info; 396*cf21f328SLaurent Pinchart unsigned int index; 397*cf21f328SLaurent Pinchart unsigned int i; 398*cf21f328SLaurent Pinchart 399*cf21f328SLaurent Pinchart if (code->pad == MXC_ISI_PIPE_PAD_SOURCE) { 400*cf21f328SLaurent Pinchart const struct v4l2_mbus_framefmt *format; 401*cf21f328SLaurent Pinchart 402*cf21f328SLaurent Pinchart format = mxc_isi_pipe_get_pad_format(pipe, state, 403*cf21f328SLaurent Pinchart MXC_ISI_PIPE_PAD_SINK); 404*cf21f328SLaurent Pinchart info = mxc_isi_bus_format_by_code(format->code, 405*cf21f328SLaurent Pinchart MXC_ISI_PIPE_PAD_SINK); 406*cf21f328SLaurent Pinchart 407*cf21f328SLaurent Pinchart if (info->encoding == MXC_ISI_ENC_RAW) { 408*cf21f328SLaurent Pinchart /* 409*cf21f328SLaurent Pinchart * For RAW formats, the sink and source media bus codes 410*cf21f328SLaurent Pinchart * must match. 411*cf21f328SLaurent Pinchart */ 412*cf21f328SLaurent Pinchart if (code->index) 413*cf21f328SLaurent Pinchart return -EINVAL; 414*cf21f328SLaurent Pinchart 415*cf21f328SLaurent Pinchart code->code = info->output; 416*cf21f328SLaurent Pinchart } else { 417*cf21f328SLaurent Pinchart /* 418*cf21f328SLaurent Pinchart * For RGB or YUV formats, the ISI supports format 419*cf21f328SLaurent Pinchart * conversion. Either of the two output formats can be 420*cf21f328SLaurent Pinchart * used regardless of the input. 421*cf21f328SLaurent Pinchart */ 422*cf21f328SLaurent Pinchart if (code->index > 1) 423*cf21f328SLaurent Pinchart return -EINVAL; 424*cf21f328SLaurent Pinchart 425*cf21f328SLaurent Pinchart code->code = output_codes[code->index]; 426*cf21f328SLaurent Pinchart } 427*cf21f328SLaurent Pinchart 428*cf21f328SLaurent Pinchart return 0; 429*cf21f328SLaurent Pinchart } 430*cf21f328SLaurent Pinchart 431*cf21f328SLaurent Pinchart index = code->index; 432*cf21f328SLaurent Pinchart 433*cf21f328SLaurent Pinchart for (i = 0; i < ARRAY_SIZE(mxc_isi_bus_formats); ++i) { 434*cf21f328SLaurent Pinchart info = &mxc_isi_bus_formats[i]; 435*cf21f328SLaurent Pinchart 436*cf21f328SLaurent Pinchart if (!(info->pads & BIT(MXC_ISI_PIPE_PAD_SINK))) 437*cf21f328SLaurent Pinchart continue; 438*cf21f328SLaurent Pinchart 439*cf21f328SLaurent Pinchart if (index == 0) { 440*cf21f328SLaurent Pinchart code->code = info->mbus_code; 441*cf21f328SLaurent Pinchart return 0; 442*cf21f328SLaurent Pinchart } 443*cf21f328SLaurent Pinchart 444*cf21f328SLaurent Pinchart index--; 445*cf21f328SLaurent Pinchart } 446*cf21f328SLaurent Pinchart 447*cf21f328SLaurent Pinchart return -EINVAL; 448*cf21f328SLaurent Pinchart } 449*cf21f328SLaurent Pinchart 450*cf21f328SLaurent Pinchart static int mxc_isi_pipe_set_fmt(struct v4l2_subdev *sd, 451*cf21f328SLaurent Pinchart struct v4l2_subdev_state *state, 452*cf21f328SLaurent Pinchart struct v4l2_subdev_format *fmt) 453*cf21f328SLaurent Pinchart { 454*cf21f328SLaurent Pinchart struct mxc_isi_pipe *pipe = to_isi_pipe(sd); 455*cf21f328SLaurent Pinchart struct v4l2_mbus_framefmt *mf = &fmt->format; 456*cf21f328SLaurent Pinchart const struct mxc_isi_bus_format_info *info; 457*cf21f328SLaurent Pinchart struct v4l2_mbus_framefmt *format; 458*cf21f328SLaurent Pinchart struct v4l2_rect *rect; 459*cf21f328SLaurent Pinchart 460*cf21f328SLaurent Pinchart if (vb2_is_busy(&pipe->video.vb2_q)) 461*cf21f328SLaurent Pinchart return -EBUSY; 462*cf21f328SLaurent Pinchart 463*cf21f328SLaurent Pinchart if (fmt->pad == MXC_ISI_PIPE_PAD_SINK) { 464*cf21f328SLaurent Pinchart unsigned int max_width; 465*cf21f328SLaurent Pinchart 466*cf21f328SLaurent Pinchart info = mxc_isi_bus_format_by_code(mf->code, 467*cf21f328SLaurent Pinchart MXC_ISI_PIPE_PAD_SINK); 468*cf21f328SLaurent Pinchart if (!info) 469*cf21f328SLaurent Pinchart info = mxc_isi_bus_format_by_code(MXC_ISI_DEF_MBUS_CODE_SINK, 470*cf21f328SLaurent Pinchart MXC_ISI_PIPE_PAD_SINK); 471*cf21f328SLaurent Pinchart 472*cf21f328SLaurent Pinchart /* 473*cf21f328SLaurent Pinchart * Limit the max line length if there's no adjacent pipe to 474*cf21f328SLaurent Pinchart * chain with. 475*cf21f328SLaurent Pinchart */ 476*cf21f328SLaurent Pinchart max_width = pipe->id == pipe->isi->pdata->num_channels - 1 477*cf21f328SLaurent Pinchart ? MXC_ISI_MAX_WIDTH_UNCHAINED 478*cf21f328SLaurent Pinchart : MXC_ISI_MAX_WIDTH_CHAINED; 479*cf21f328SLaurent Pinchart 480*cf21f328SLaurent Pinchart mf->code = info->mbus_code; 481*cf21f328SLaurent Pinchart mf->width = clamp(mf->width, MXC_ISI_MIN_WIDTH, max_width); 482*cf21f328SLaurent Pinchart mf->height = clamp(mf->height, MXC_ISI_MIN_HEIGHT, 483*cf21f328SLaurent Pinchart MXC_ISI_MAX_HEIGHT); 484*cf21f328SLaurent Pinchart 485*cf21f328SLaurent Pinchart /* Propagate the format to the source pad. */ 486*cf21f328SLaurent Pinchart rect = mxc_isi_pipe_get_pad_compose(pipe, state, 487*cf21f328SLaurent Pinchart MXC_ISI_PIPE_PAD_SINK); 488*cf21f328SLaurent Pinchart rect->width = mf->width; 489*cf21f328SLaurent Pinchart rect->height = mf->height; 490*cf21f328SLaurent Pinchart 491*cf21f328SLaurent Pinchart rect = mxc_isi_pipe_get_pad_crop(pipe, state, 492*cf21f328SLaurent Pinchart MXC_ISI_PIPE_PAD_SOURCE); 493*cf21f328SLaurent Pinchart rect->left = 0; 494*cf21f328SLaurent Pinchart rect->top = 0; 495*cf21f328SLaurent Pinchart rect->width = mf->width; 496*cf21f328SLaurent Pinchart rect->height = mf->height; 497*cf21f328SLaurent Pinchart 498*cf21f328SLaurent Pinchart format = mxc_isi_pipe_get_pad_format(pipe, state, 499*cf21f328SLaurent Pinchart MXC_ISI_PIPE_PAD_SOURCE); 500*cf21f328SLaurent Pinchart format->code = info->output; 501*cf21f328SLaurent Pinchart format->width = mf->width; 502*cf21f328SLaurent Pinchart format->height = mf->height; 503*cf21f328SLaurent Pinchart } else { 504*cf21f328SLaurent Pinchart /* 505*cf21f328SLaurent Pinchart * For RGB or YUV formats, the ISI supports RGB <-> YUV format 506*cf21f328SLaurent Pinchart * conversion. For RAW formats, the sink and source media bus 507*cf21f328SLaurent Pinchart * codes must match. 508*cf21f328SLaurent Pinchart */ 509*cf21f328SLaurent Pinchart format = mxc_isi_pipe_get_pad_format(pipe, state, 510*cf21f328SLaurent Pinchart MXC_ISI_PIPE_PAD_SINK); 511*cf21f328SLaurent Pinchart info = mxc_isi_bus_format_by_code(format->code, 512*cf21f328SLaurent Pinchart MXC_ISI_PIPE_PAD_SINK); 513*cf21f328SLaurent Pinchart 514*cf21f328SLaurent Pinchart if (info->encoding != MXC_ISI_ENC_RAW) { 515*cf21f328SLaurent Pinchart if (mf->code != MEDIA_BUS_FMT_YUV8_1X24 && 516*cf21f328SLaurent Pinchart mf->code != MEDIA_BUS_FMT_RGB888_1X24) 517*cf21f328SLaurent Pinchart mf->code = info->output; 518*cf21f328SLaurent Pinchart 519*cf21f328SLaurent Pinchart info = mxc_isi_bus_format_by_code(mf->code, 520*cf21f328SLaurent Pinchart MXC_ISI_PIPE_PAD_SOURCE); 521*cf21f328SLaurent Pinchart } 522*cf21f328SLaurent Pinchart 523*cf21f328SLaurent Pinchart mf->code = info->output; 524*cf21f328SLaurent Pinchart 525*cf21f328SLaurent Pinchart /* 526*cf21f328SLaurent Pinchart * The width and height on the source can't be changed, they 527*cf21f328SLaurent Pinchart * must match the crop rectangle size. 528*cf21f328SLaurent Pinchart */ 529*cf21f328SLaurent Pinchart rect = mxc_isi_pipe_get_pad_crop(pipe, state, 530*cf21f328SLaurent Pinchart MXC_ISI_PIPE_PAD_SOURCE); 531*cf21f328SLaurent Pinchart 532*cf21f328SLaurent Pinchart mf->width = rect->width; 533*cf21f328SLaurent Pinchart mf->height = rect->height; 534*cf21f328SLaurent Pinchart } 535*cf21f328SLaurent Pinchart 536*cf21f328SLaurent Pinchart format = mxc_isi_pipe_get_pad_format(pipe, state, fmt->pad); 537*cf21f328SLaurent Pinchart *format = *mf; 538*cf21f328SLaurent Pinchart 539*cf21f328SLaurent Pinchart dev_dbg(pipe->isi->dev, "pad%u: code: 0x%04x, %ux%u", 540*cf21f328SLaurent Pinchart fmt->pad, mf->code, mf->width, mf->height); 541*cf21f328SLaurent Pinchart 542*cf21f328SLaurent Pinchart return 0; 543*cf21f328SLaurent Pinchart } 544*cf21f328SLaurent Pinchart 545*cf21f328SLaurent Pinchart static int mxc_isi_pipe_get_selection(struct v4l2_subdev *sd, 546*cf21f328SLaurent Pinchart struct v4l2_subdev_state *state, 547*cf21f328SLaurent Pinchart struct v4l2_subdev_selection *sel) 548*cf21f328SLaurent Pinchart { 549*cf21f328SLaurent Pinchart struct mxc_isi_pipe *pipe = to_isi_pipe(sd); 550*cf21f328SLaurent Pinchart const struct v4l2_mbus_framefmt *format; 551*cf21f328SLaurent Pinchart const struct v4l2_rect *rect; 552*cf21f328SLaurent Pinchart 553*cf21f328SLaurent Pinchart switch (sel->target) { 554*cf21f328SLaurent Pinchart case V4L2_SEL_TGT_COMPOSE_BOUNDS: 555*cf21f328SLaurent Pinchart if (sel->pad != MXC_ISI_PIPE_PAD_SINK) 556*cf21f328SLaurent Pinchart /* No compose rectangle on source pad. */ 557*cf21f328SLaurent Pinchart return -EINVAL; 558*cf21f328SLaurent Pinchart 559*cf21f328SLaurent Pinchart /* The sink compose is bound by the sink format. */ 560*cf21f328SLaurent Pinchart format = mxc_isi_pipe_get_pad_format(pipe, state, 561*cf21f328SLaurent Pinchart MXC_ISI_PIPE_PAD_SINK); 562*cf21f328SLaurent Pinchart sel->r.left = 0; 563*cf21f328SLaurent Pinchart sel->r.top = 0; 564*cf21f328SLaurent Pinchart sel->r.width = format->width; 565*cf21f328SLaurent Pinchart sel->r.height = format->height; 566*cf21f328SLaurent Pinchart break; 567*cf21f328SLaurent Pinchart 568*cf21f328SLaurent Pinchart case V4L2_SEL_TGT_CROP_BOUNDS: 569*cf21f328SLaurent Pinchart if (sel->pad != MXC_ISI_PIPE_PAD_SOURCE) 570*cf21f328SLaurent Pinchart /* No crop rectangle on sink pad. */ 571*cf21f328SLaurent Pinchart return -EINVAL; 572*cf21f328SLaurent Pinchart 573*cf21f328SLaurent Pinchart /* The source crop is bound by the sink compose. */ 574*cf21f328SLaurent Pinchart rect = mxc_isi_pipe_get_pad_compose(pipe, state, 575*cf21f328SLaurent Pinchart MXC_ISI_PIPE_PAD_SINK); 576*cf21f328SLaurent Pinchart sel->r = *rect; 577*cf21f328SLaurent Pinchart break; 578*cf21f328SLaurent Pinchart 579*cf21f328SLaurent Pinchart case V4L2_SEL_TGT_CROP: 580*cf21f328SLaurent Pinchart if (sel->pad != MXC_ISI_PIPE_PAD_SOURCE) 581*cf21f328SLaurent Pinchart /* No crop rectangle on sink pad. */ 582*cf21f328SLaurent Pinchart return -EINVAL; 583*cf21f328SLaurent Pinchart 584*cf21f328SLaurent Pinchart rect = mxc_isi_pipe_get_pad_crop(pipe, state, sel->pad); 585*cf21f328SLaurent Pinchart sel->r = *rect; 586*cf21f328SLaurent Pinchart break; 587*cf21f328SLaurent Pinchart 588*cf21f328SLaurent Pinchart case V4L2_SEL_TGT_COMPOSE: 589*cf21f328SLaurent Pinchart if (sel->pad != MXC_ISI_PIPE_PAD_SINK) 590*cf21f328SLaurent Pinchart /* No compose rectangle on source pad. */ 591*cf21f328SLaurent Pinchart return -EINVAL; 592*cf21f328SLaurent Pinchart 593*cf21f328SLaurent Pinchart rect = mxc_isi_pipe_get_pad_compose(pipe, state, sel->pad); 594*cf21f328SLaurent Pinchart sel->r = *rect; 595*cf21f328SLaurent Pinchart break; 596*cf21f328SLaurent Pinchart 597*cf21f328SLaurent Pinchart default: 598*cf21f328SLaurent Pinchart return -EINVAL; 599*cf21f328SLaurent Pinchart } 600*cf21f328SLaurent Pinchart 601*cf21f328SLaurent Pinchart return 0; 602*cf21f328SLaurent Pinchart } 603*cf21f328SLaurent Pinchart 604*cf21f328SLaurent Pinchart static int mxc_isi_pipe_set_selection(struct v4l2_subdev *sd, 605*cf21f328SLaurent Pinchart struct v4l2_subdev_state *state, 606*cf21f328SLaurent Pinchart struct v4l2_subdev_selection *sel) 607*cf21f328SLaurent Pinchart { 608*cf21f328SLaurent Pinchart struct mxc_isi_pipe *pipe = to_isi_pipe(sd); 609*cf21f328SLaurent Pinchart struct v4l2_mbus_framefmt *format; 610*cf21f328SLaurent Pinchart struct v4l2_rect *rect; 611*cf21f328SLaurent Pinchart 612*cf21f328SLaurent Pinchart switch (sel->target) { 613*cf21f328SLaurent Pinchart case V4L2_SEL_TGT_CROP: 614*cf21f328SLaurent Pinchart if (sel->pad != MXC_ISI_PIPE_PAD_SOURCE) 615*cf21f328SLaurent Pinchart /* The pipeline support cropping on the source only. */ 616*cf21f328SLaurent Pinchart return -EINVAL; 617*cf21f328SLaurent Pinchart 618*cf21f328SLaurent Pinchart /* The source crop is bound by the sink compose. */ 619*cf21f328SLaurent Pinchart rect = mxc_isi_pipe_get_pad_compose(pipe, state, 620*cf21f328SLaurent Pinchart MXC_ISI_PIPE_PAD_SINK); 621*cf21f328SLaurent Pinchart sel->r.left = clamp_t(s32, sel->r.left, 0, rect->width - 1); 622*cf21f328SLaurent Pinchart sel->r.top = clamp_t(s32, sel->r.top, 0, rect->height - 1); 623*cf21f328SLaurent Pinchart sel->r.width = clamp(sel->r.width, MXC_ISI_MIN_WIDTH, 624*cf21f328SLaurent Pinchart rect->width - sel->r.left); 625*cf21f328SLaurent Pinchart sel->r.height = clamp(sel->r.height, MXC_ISI_MIN_HEIGHT, 626*cf21f328SLaurent Pinchart rect->height - sel->r.top); 627*cf21f328SLaurent Pinchart 628*cf21f328SLaurent Pinchart rect = mxc_isi_pipe_get_pad_crop(pipe, state, 629*cf21f328SLaurent Pinchart MXC_ISI_PIPE_PAD_SOURCE); 630*cf21f328SLaurent Pinchart *rect = sel->r; 631*cf21f328SLaurent Pinchart 632*cf21f328SLaurent Pinchart /* Propagate the crop rectangle to the source pad. */ 633*cf21f328SLaurent Pinchart format = mxc_isi_pipe_get_pad_format(pipe, state, 634*cf21f328SLaurent Pinchart MXC_ISI_PIPE_PAD_SOURCE); 635*cf21f328SLaurent Pinchart format->width = sel->r.width; 636*cf21f328SLaurent Pinchart format->height = sel->r.height; 637*cf21f328SLaurent Pinchart break; 638*cf21f328SLaurent Pinchart 639*cf21f328SLaurent Pinchart case V4L2_SEL_TGT_COMPOSE: 640*cf21f328SLaurent Pinchart if (sel->pad != MXC_ISI_PIPE_PAD_SINK) 641*cf21f328SLaurent Pinchart /* Composing is supported on the sink only. */ 642*cf21f328SLaurent Pinchart return -EINVAL; 643*cf21f328SLaurent Pinchart 644*cf21f328SLaurent Pinchart /* The sink crop is bound by the sink format downscaling only). */ 645*cf21f328SLaurent Pinchart format = mxc_isi_pipe_get_pad_format(pipe, state, 646*cf21f328SLaurent Pinchart MXC_ISI_PIPE_PAD_SINK); 647*cf21f328SLaurent Pinchart 648*cf21f328SLaurent Pinchart sel->r.left = 0; 649*cf21f328SLaurent Pinchart sel->r.top = 0; 650*cf21f328SLaurent Pinchart sel->r.width = clamp(sel->r.width, MXC_ISI_MIN_WIDTH, 651*cf21f328SLaurent Pinchart format->width); 652*cf21f328SLaurent Pinchart sel->r.height = clamp(sel->r.height, MXC_ISI_MIN_HEIGHT, 653*cf21f328SLaurent Pinchart format->height); 654*cf21f328SLaurent Pinchart 655*cf21f328SLaurent Pinchart rect = mxc_isi_pipe_get_pad_compose(pipe, state, 656*cf21f328SLaurent Pinchart MXC_ISI_PIPE_PAD_SINK); 657*cf21f328SLaurent Pinchart *rect = sel->r; 658*cf21f328SLaurent Pinchart 659*cf21f328SLaurent Pinchart /* Propagate the compose rectangle to the source pad. */ 660*cf21f328SLaurent Pinchart rect = mxc_isi_pipe_get_pad_crop(pipe, state, 661*cf21f328SLaurent Pinchart MXC_ISI_PIPE_PAD_SOURCE); 662*cf21f328SLaurent Pinchart rect->left = 0; 663*cf21f328SLaurent Pinchart rect->top = 0; 664*cf21f328SLaurent Pinchart rect->width = sel->r.width; 665*cf21f328SLaurent Pinchart rect->height = sel->r.height; 666*cf21f328SLaurent Pinchart 667*cf21f328SLaurent Pinchart format = mxc_isi_pipe_get_pad_format(pipe, state, 668*cf21f328SLaurent Pinchart MXC_ISI_PIPE_PAD_SOURCE); 669*cf21f328SLaurent Pinchart format->width = sel->r.width; 670*cf21f328SLaurent Pinchart format->height = sel->r.height; 671*cf21f328SLaurent Pinchart break; 672*cf21f328SLaurent Pinchart 673*cf21f328SLaurent Pinchart default: 674*cf21f328SLaurent Pinchart return -EINVAL; 675*cf21f328SLaurent Pinchart } 676*cf21f328SLaurent Pinchart 677*cf21f328SLaurent Pinchart dev_dbg(pipe->isi->dev, "%s, target %#x: (%d,%d)/%dx%d", __func__, 678*cf21f328SLaurent Pinchart sel->target, sel->r.left, sel->r.top, sel->r.width, 679*cf21f328SLaurent Pinchart sel->r.height); 680*cf21f328SLaurent Pinchart 681*cf21f328SLaurent Pinchart return 0; 682*cf21f328SLaurent Pinchart } 683*cf21f328SLaurent Pinchart 684*cf21f328SLaurent Pinchart static const struct v4l2_subdev_pad_ops mxc_isi_pipe_subdev_pad_ops = { 685*cf21f328SLaurent Pinchart .init_cfg = mxc_isi_pipe_init_cfg, 686*cf21f328SLaurent Pinchart .enum_mbus_code = mxc_isi_pipe_enum_mbus_code, 687*cf21f328SLaurent Pinchart .get_fmt = v4l2_subdev_get_fmt, 688*cf21f328SLaurent Pinchart .set_fmt = mxc_isi_pipe_set_fmt, 689*cf21f328SLaurent Pinchart .get_selection = mxc_isi_pipe_get_selection, 690*cf21f328SLaurent Pinchart .set_selection = mxc_isi_pipe_set_selection, 691*cf21f328SLaurent Pinchart }; 692*cf21f328SLaurent Pinchart 693*cf21f328SLaurent Pinchart static const struct v4l2_subdev_ops mxc_isi_pipe_subdev_ops = { 694*cf21f328SLaurent Pinchart .pad = &mxc_isi_pipe_subdev_pad_ops, 695*cf21f328SLaurent Pinchart }; 696*cf21f328SLaurent Pinchart 697*cf21f328SLaurent Pinchart /* ----------------------------------------------------------------------------- 698*cf21f328SLaurent Pinchart * IRQ handling 699*cf21f328SLaurent Pinchart */ 700*cf21f328SLaurent Pinchart 701*cf21f328SLaurent Pinchart static irqreturn_t mxc_isi_pipe_irq_handler(int irq, void *priv) 702*cf21f328SLaurent Pinchart { 703*cf21f328SLaurent Pinchart struct mxc_isi_pipe *pipe = priv; 704*cf21f328SLaurent Pinchart const struct mxc_isi_ier_reg *ier_reg = pipe->isi->pdata->ier_reg; 705*cf21f328SLaurent Pinchart u32 status; 706*cf21f328SLaurent Pinchart 707*cf21f328SLaurent Pinchart status = mxc_isi_channel_irq_status(pipe, true); 708*cf21f328SLaurent Pinchart 709*cf21f328SLaurent Pinchart if (status & CHNL_STS_FRM_STRD) { 710*cf21f328SLaurent Pinchart if (!WARN_ON(!pipe->irq_handler)) 711*cf21f328SLaurent Pinchart pipe->irq_handler(pipe, status); 712*cf21f328SLaurent Pinchart } 713*cf21f328SLaurent Pinchart 714*cf21f328SLaurent Pinchart if (status & (CHNL_STS_AXI_WR_ERR_Y | 715*cf21f328SLaurent Pinchart CHNL_STS_AXI_WR_ERR_U | 716*cf21f328SLaurent Pinchart CHNL_STS_AXI_WR_ERR_V)) 717*cf21f328SLaurent Pinchart dev_dbg(pipe->isi->dev, "%s: IRQ AXI Error stat=0x%X\n", 718*cf21f328SLaurent Pinchart __func__, status); 719*cf21f328SLaurent Pinchart 720*cf21f328SLaurent Pinchart if (status & (ier_reg->panic_y_buf_en.mask | 721*cf21f328SLaurent Pinchart ier_reg->panic_u_buf_en.mask | 722*cf21f328SLaurent Pinchart ier_reg->panic_v_buf_en.mask)) 723*cf21f328SLaurent Pinchart dev_dbg(pipe->isi->dev, "%s: IRQ Panic OFLW Error stat=0x%X\n", 724*cf21f328SLaurent Pinchart __func__, status); 725*cf21f328SLaurent Pinchart 726*cf21f328SLaurent Pinchart if (status & (ier_reg->oflw_y_buf_en.mask | 727*cf21f328SLaurent Pinchart ier_reg->oflw_u_buf_en.mask | 728*cf21f328SLaurent Pinchart ier_reg->oflw_v_buf_en.mask)) 729*cf21f328SLaurent Pinchart dev_dbg(pipe->isi->dev, "%s: IRQ OFLW Error stat=0x%X\n", 730*cf21f328SLaurent Pinchart __func__, status); 731*cf21f328SLaurent Pinchart 732*cf21f328SLaurent Pinchart if (status & (ier_reg->excs_oflw_y_buf_en.mask | 733*cf21f328SLaurent Pinchart ier_reg->excs_oflw_u_buf_en.mask | 734*cf21f328SLaurent Pinchart ier_reg->excs_oflw_v_buf_en.mask)) 735*cf21f328SLaurent Pinchart dev_dbg(pipe->isi->dev, "%s: IRQ EXCS OFLW Error stat=0x%X\n", 736*cf21f328SLaurent Pinchart __func__, status); 737*cf21f328SLaurent Pinchart 738*cf21f328SLaurent Pinchart return IRQ_HANDLED; 739*cf21f328SLaurent Pinchart } 740*cf21f328SLaurent Pinchart 741*cf21f328SLaurent Pinchart /* ----------------------------------------------------------------------------- 742*cf21f328SLaurent Pinchart * Init & cleanup 743*cf21f328SLaurent Pinchart */ 744*cf21f328SLaurent Pinchart 745*cf21f328SLaurent Pinchart static const struct media_entity_operations mxc_isi_pipe_entity_ops = { 746*cf21f328SLaurent Pinchart .link_validate = v4l2_subdev_link_validate, 747*cf21f328SLaurent Pinchart }; 748*cf21f328SLaurent Pinchart 749*cf21f328SLaurent Pinchart int mxc_isi_pipe_init(struct mxc_isi_dev *isi, unsigned int id) 750*cf21f328SLaurent Pinchart { 751*cf21f328SLaurent Pinchart struct mxc_isi_pipe *pipe = &isi->pipes[id]; 752*cf21f328SLaurent Pinchart struct v4l2_subdev *sd; 753*cf21f328SLaurent Pinchart int irq; 754*cf21f328SLaurent Pinchart int ret; 755*cf21f328SLaurent Pinchart 756*cf21f328SLaurent Pinchart pipe->id = id; 757*cf21f328SLaurent Pinchart pipe->isi = isi; 758*cf21f328SLaurent Pinchart pipe->regs = isi->regs + id * isi->pdata->reg_offset; 759*cf21f328SLaurent Pinchart 760*cf21f328SLaurent Pinchart mutex_init(&pipe->lock); 761*cf21f328SLaurent Pinchart 762*cf21f328SLaurent Pinchart pipe->available_res = MXC_ISI_CHANNEL_RES_LINE_BUF 763*cf21f328SLaurent Pinchart | MXC_ISI_CHANNEL_RES_OUTPUT_BUF; 764*cf21f328SLaurent Pinchart pipe->acquired_res = 0; 765*cf21f328SLaurent Pinchart pipe->chained_res = 0; 766*cf21f328SLaurent Pinchart pipe->chained = false; 767*cf21f328SLaurent Pinchart 768*cf21f328SLaurent Pinchart sd = &pipe->sd; 769*cf21f328SLaurent Pinchart v4l2_subdev_init(sd, &mxc_isi_pipe_subdev_ops); 770*cf21f328SLaurent Pinchart sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; 771*cf21f328SLaurent Pinchart snprintf(sd->name, sizeof(sd->name), "mxc_isi.%d", pipe->id); 772*cf21f328SLaurent Pinchart sd->dev = isi->dev; 773*cf21f328SLaurent Pinchart 774*cf21f328SLaurent Pinchart sd->entity.function = MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER; 775*cf21f328SLaurent Pinchart sd->entity.ops = &mxc_isi_pipe_entity_ops; 776*cf21f328SLaurent Pinchart 777*cf21f328SLaurent Pinchart pipe->pads[MXC_ISI_PIPE_PAD_SINK].flags = MEDIA_PAD_FL_SINK; 778*cf21f328SLaurent Pinchart pipe->pads[MXC_ISI_PIPE_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; 779*cf21f328SLaurent Pinchart 780*cf21f328SLaurent Pinchart ret = media_entity_pads_init(&sd->entity, MXC_ISI_PIPE_PADS_NUM, 781*cf21f328SLaurent Pinchart pipe->pads); 782*cf21f328SLaurent Pinchart if (ret) 783*cf21f328SLaurent Pinchart goto error; 784*cf21f328SLaurent Pinchart 785*cf21f328SLaurent Pinchart ret = v4l2_subdev_init_finalize(sd); 786*cf21f328SLaurent Pinchart if (ret < 0) 787*cf21f328SLaurent Pinchart goto error; 788*cf21f328SLaurent Pinchart 789*cf21f328SLaurent Pinchart /* Register IRQ handler. */ 790*cf21f328SLaurent Pinchart mxc_isi_channel_irq_clear(pipe); 791*cf21f328SLaurent Pinchart 792*cf21f328SLaurent Pinchart irq = platform_get_irq(to_platform_device(isi->dev), id); 793*cf21f328SLaurent Pinchart if (irq < 0) { 794*cf21f328SLaurent Pinchart dev_err(pipe->isi->dev, "Failed to get IRQ (%d)\n", irq); 795*cf21f328SLaurent Pinchart ret = irq; 796*cf21f328SLaurent Pinchart goto error; 797*cf21f328SLaurent Pinchart } 798*cf21f328SLaurent Pinchart 799*cf21f328SLaurent Pinchart ret = devm_request_irq(isi->dev, irq, mxc_isi_pipe_irq_handler, 800*cf21f328SLaurent Pinchart 0, dev_name(isi->dev), pipe); 801*cf21f328SLaurent Pinchart if (ret < 0) { 802*cf21f328SLaurent Pinchart dev_err(isi->dev, "failed to request IRQ (%d)\n", ret); 803*cf21f328SLaurent Pinchart goto error; 804*cf21f328SLaurent Pinchart } 805*cf21f328SLaurent Pinchart 806*cf21f328SLaurent Pinchart return 0; 807*cf21f328SLaurent Pinchart 808*cf21f328SLaurent Pinchart error: 809*cf21f328SLaurent Pinchart media_entity_cleanup(&sd->entity); 810*cf21f328SLaurent Pinchart mutex_destroy(&pipe->lock); 811*cf21f328SLaurent Pinchart 812*cf21f328SLaurent Pinchart return ret; 813*cf21f328SLaurent Pinchart } 814*cf21f328SLaurent Pinchart 815*cf21f328SLaurent Pinchart void mxc_isi_pipe_cleanup(struct mxc_isi_pipe *pipe) 816*cf21f328SLaurent Pinchart { 817*cf21f328SLaurent Pinchart struct v4l2_subdev *sd = &pipe->sd; 818*cf21f328SLaurent Pinchart 819*cf21f328SLaurent Pinchart media_entity_cleanup(&sd->entity); 820*cf21f328SLaurent Pinchart mutex_destroy(&pipe->lock); 821*cf21f328SLaurent Pinchart } 822*cf21f328SLaurent Pinchart 823*cf21f328SLaurent Pinchart int mxc_isi_pipe_acquire(struct mxc_isi_pipe *pipe, 824*cf21f328SLaurent Pinchart mxc_isi_pipe_irq_t irq_handler) 825*cf21f328SLaurent Pinchart { 826*cf21f328SLaurent Pinchart const struct mxc_isi_bus_format_info *sink_info; 827*cf21f328SLaurent Pinchart const struct mxc_isi_bus_format_info *src_info; 828*cf21f328SLaurent Pinchart struct v4l2_mbus_framefmt *sink_fmt; 829*cf21f328SLaurent Pinchart const struct v4l2_mbus_framefmt *src_fmt; 830*cf21f328SLaurent Pinchart struct v4l2_subdev *sd = &pipe->sd; 831*cf21f328SLaurent Pinchart struct v4l2_subdev_state *state; 832*cf21f328SLaurent Pinchart bool bypass; 833*cf21f328SLaurent Pinchart int ret; 834*cf21f328SLaurent Pinchart 835*cf21f328SLaurent Pinchart state = v4l2_subdev_lock_and_get_active_state(sd); 836*cf21f328SLaurent Pinchart sink_fmt = v4l2_subdev_get_try_format(sd, state, MXC_ISI_PIPE_PAD_SINK); 837*cf21f328SLaurent Pinchart src_fmt = v4l2_subdev_get_try_format(sd, state, MXC_ISI_PIPE_PAD_SOURCE); 838*cf21f328SLaurent Pinchart v4l2_subdev_unlock_state(state); 839*cf21f328SLaurent Pinchart 840*cf21f328SLaurent Pinchart sink_info = mxc_isi_bus_format_by_code(sink_fmt->code, 841*cf21f328SLaurent Pinchart MXC_ISI_PIPE_PAD_SINK); 842*cf21f328SLaurent Pinchart src_info = mxc_isi_bus_format_by_code(src_fmt->code, 843*cf21f328SLaurent Pinchart MXC_ISI_PIPE_PAD_SOURCE); 844*cf21f328SLaurent Pinchart 845*cf21f328SLaurent Pinchart bypass = sink_fmt->width == src_fmt->width && 846*cf21f328SLaurent Pinchart sink_fmt->height == src_fmt->height && 847*cf21f328SLaurent Pinchart sink_info->encoding == src_info->encoding; 848*cf21f328SLaurent Pinchart 849*cf21f328SLaurent Pinchart ret = mxc_isi_channel_acquire(pipe, irq_handler, bypass); 850*cf21f328SLaurent Pinchart if (ret) 851*cf21f328SLaurent Pinchart return ret; 852*cf21f328SLaurent Pinchart 853*cf21f328SLaurent Pinchart /* Chain the channel if needed for wide resolutions. */ 854*cf21f328SLaurent Pinchart if (sink_fmt->width > MXC_ISI_MAX_WIDTH_UNCHAINED) { 855*cf21f328SLaurent Pinchart ret = mxc_isi_channel_chain(pipe, bypass); 856*cf21f328SLaurent Pinchart if (ret) 857*cf21f328SLaurent Pinchart mxc_isi_channel_release(pipe); 858*cf21f328SLaurent Pinchart } 859*cf21f328SLaurent Pinchart 860*cf21f328SLaurent Pinchart return ret; 861*cf21f328SLaurent Pinchart } 862*cf21f328SLaurent Pinchart 863*cf21f328SLaurent Pinchart void mxc_isi_pipe_release(struct mxc_isi_pipe *pipe) 864*cf21f328SLaurent Pinchart { 865*cf21f328SLaurent Pinchart mxc_isi_channel_release(pipe); 866*cf21f328SLaurent Pinchart mxc_isi_channel_unchain(pipe); 867*cf21f328SLaurent Pinchart } 868