1*6edb685aSTomi Valkeinen // SPDX-License-Identifier: GPL-2.0 2*6edb685aSTomi Valkeinen /* 3*6edb685aSTomi Valkeinen * PiSP Front End Driver 4*6edb685aSTomi Valkeinen * 5*6edb685aSTomi Valkeinen * Copyright (c) 2021-2024 Raspberry Pi Ltd. 6*6edb685aSTomi Valkeinen */ 7*6edb685aSTomi Valkeinen 8*6edb685aSTomi Valkeinen #include <linux/bitops.h> 9*6edb685aSTomi Valkeinen #include <linux/delay.h> 10*6edb685aSTomi Valkeinen #include <linux/moduleparam.h> 11*6edb685aSTomi Valkeinen #include <linux/pm_runtime.h> 12*6edb685aSTomi Valkeinen #include <linux/seq_file.h> 13*6edb685aSTomi Valkeinen 14*6edb685aSTomi Valkeinen #include <media/videobuf2-dma-contig.h> 15*6edb685aSTomi Valkeinen 16*6edb685aSTomi Valkeinen #include "cfe.h" 17*6edb685aSTomi Valkeinen #include "pisp-fe.h" 18*6edb685aSTomi Valkeinen 19*6edb685aSTomi Valkeinen #include "cfe-trace.h" 20*6edb685aSTomi Valkeinen 21*6edb685aSTomi Valkeinen #define FE_VERSION 0x000 22*6edb685aSTomi Valkeinen #define FE_CONTROL 0x004 23*6edb685aSTomi Valkeinen #define FE_STATUS 0x008 24*6edb685aSTomi Valkeinen #define FE_FRAME_STATUS 0x00c 25*6edb685aSTomi Valkeinen #define FE_ERROR_STATUS 0x010 26*6edb685aSTomi Valkeinen #define FE_OUTPUT_STATUS 0x014 27*6edb685aSTomi Valkeinen #define FE_INT_EN 0x018 28*6edb685aSTomi Valkeinen #define FE_INT_STATUS 0x01c 29*6edb685aSTomi Valkeinen 30*6edb685aSTomi Valkeinen /* CONTROL */ 31*6edb685aSTomi Valkeinen #define FE_CONTROL_QUEUE BIT(0) 32*6edb685aSTomi Valkeinen #define FE_CONTROL_ABORT BIT(1) 33*6edb685aSTomi Valkeinen #define FE_CONTROL_RESET BIT(2) 34*6edb685aSTomi Valkeinen #define FE_CONTROL_LATCH_REGS BIT(3) 35*6edb685aSTomi Valkeinen 36*6edb685aSTomi Valkeinen /* INT_EN / INT_STATUS */ 37*6edb685aSTomi Valkeinen #define FE_INT_EOF BIT(0) 38*6edb685aSTomi Valkeinen #define FE_INT_SOF BIT(1) 39*6edb685aSTomi Valkeinen #define FE_INT_LINES0 BIT(8) 40*6edb685aSTomi Valkeinen #define FE_INT_LINES1 BIT(9) 41*6edb685aSTomi Valkeinen #define FE_INT_STATS BIT(16) 42*6edb685aSTomi Valkeinen #define FE_INT_QREADY BIT(24) 43*6edb685aSTomi Valkeinen 44*6edb685aSTomi Valkeinen /* STATUS */ 45*6edb685aSTomi Valkeinen #define FE_STATUS_QUEUED BIT(0) 46*6edb685aSTomi Valkeinen #define FE_STATUS_WAITING BIT(1) 47*6edb685aSTomi Valkeinen #define FE_STATUS_ACTIVE BIT(2) 48*6edb685aSTomi Valkeinen 49*6edb685aSTomi Valkeinen #define PISP_FE_CONFIG_BASE_OFFSET 0x0040 50*6edb685aSTomi Valkeinen 51*6edb685aSTomi Valkeinen #define PISP_FE_ENABLE_STATS_CLUSTER \ 52*6edb685aSTomi Valkeinen (PISP_FE_ENABLE_STATS_CROP | PISP_FE_ENABLE_DECIMATE | \ 53*6edb685aSTomi Valkeinen PISP_FE_ENABLE_BLC | PISP_FE_ENABLE_CDAF_STATS | \ 54*6edb685aSTomi Valkeinen PISP_FE_ENABLE_AWB_STATS | PISP_FE_ENABLE_RGBY | \ 55*6edb685aSTomi Valkeinen PISP_FE_ENABLE_LSC | PISP_FE_ENABLE_AGC_STATS) 56*6edb685aSTomi Valkeinen 57*6edb685aSTomi Valkeinen #define PISP_FE_ENABLE_OUTPUT_CLUSTER(i) \ 58*6edb685aSTomi Valkeinen ((PISP_FE_ENABLE_CROP0 | PISP_FE_ENABLE_DOWNSCALE0 | \ 59*6edb685aSTomi Valkeinen PISP_FE_ENABLE_COMPRESS0 | PISP_FE_ENABLE_OUTPUT0) << (4 * (i))) 60*6edb685aSTomi Valkeinen 61*6edb685aSTomi Valkeinen struct pisp_fe_config_param { 62*6edb685aSTomi Valkeinen u32 dirty_flags; 63*6edb685aSTomi Valkeinen u32 dirty_flags_extra; 64*6edb685aSTomi Valkeinen size_t offset; 65*6edb685aSTomi Valkeinen size_t size; 66*6edb685aSTomi Valkeinen }; 67*6edb685aSTomi Valkeinen 68*6edb685aSTomi Valkeinen static const struct pisp_fe_config_param pisp_fe_config_map[] = { 69*6edb685aSTomi Valkeinen /* *_dirty_flag_extra types */ 70*6edb685aSTomi Valkeinen { 0, PISP_FE_DIRTY_GLOBAL, 71*6edb685aSTomi Valkeinen offsetof(struct pisp_fe_config, global), 72*6edb685aSTomi Valkeinen sizeof(struct pisp_fe_global_config) }, 73*6edb685aSTomi Valkeinen { 0, PISP_FE_DIRTY_FLOATING, 74*6edb685aSTomi Valkeinen offsetof(struct pisp_fe_config, floating_stats), 75*6edb685aSTomi Valkeinen sizeof(struct pisp_fe_floating_stats_config) }, 76*6edb685aSTomi Valkeinen { 0, PISP_FE_DIRTY_OUTPUT_AXI, 77*6edb685aSTomi Valkeinen offsetof(struct pisp_fe_config, output_axi), 78*6edb685aSTomi Valkeinen sizeof(struct pisp_fe_output_axi_config) }, 79*6edb685aSTomi Valkeinen /* *_dirty_flag types */ 80*6edb685aSTomi Valkeinen { PISP_FE_ENABLE_INPUT, 0, 81*6edb685aSTomi Valkeinen offsetof(struct pisp_fe_config, input), 82*6edb685aSTomi Valkeinen sizeof(struct pisp_fe_input_config) }, 83*6edb685aSTomi Valkeinen { PISP_FE_ENABLE_DECOMPRESS, 0, 84*6edb685aSTomi Valkeinen offsetof(struct pisp_fe_config, decompress), 85*6edb685aSTomi Valkeinen sizeof(struct pisp_decompress_config) }, 86*6edb685aSTomi Valkeinen { PISP_FE_ENABLE_DECOMPAND, 0, 87*6edb685aSTomi Valkeinen offsetof(struct pisp_fe_config, decompand), 88*6edb685aSTomi Valkeinen sizeof(struct pisp_fe_decompand_config) }, 89*6edb685aSTomi Valkeinen { PISP_FE_ENABLE_BLA, 0, 90*6edb685aSTomi Valkeinen offsetof(struct pisp_fe_config, bla), 91*6edb685aSTomi Valkeinen sizeof(struct pisp_bla_config) }, 92*6edb685aSTomi Valkeinen { PISP_FE_ENABLE_DPC, 0, 93*6edb685aSTomi Valkeinen offsetof(struct pisp_fe_config, dpc), 94*6edb685aSTomi Valkeinen sizeof(struct pisp_fe_dpc_config) }, 95*6edb685aSTomi Valkeinen { PISP_FE_ENABLE_STATS_CROP, 0, 96*6edb685aSTomi Valkeinen offsetof(struct pisp_fe_config, stats_crop), 97*6edb685aSTomi Valkeinen sizeof(struct pisp_fe_crop_config) }, 98*6edb685aSTomi Valkeinen { PISP_FE_ENABLE_BLC, 0, 99*6edb685aSTomi Valkeinen offsetof(struct pisp_fe_config, blc), 100*6edb685aSTomi Valkeinen sizeof(struct pisp_bla_config) }, 101*6edb685aSTomi Valkeinen { PISP_FE_ENABLE_CDAF_STATS, 0, 102*6edb685aSTomi Valkeinen offsetof(struct pisp_fe_config, cdaf_stats), 103*6edb685aSTomi Valkeinen sizeof(struct pisp_fe_cdaf_stats_config) }, 104*6edb685aSTomi Valkeinen { PISP_FE_ENABLE_AWB_STATS, 0, 105*6edb685aSTomi Valkeinen offsetof(struct pisp_fe_config, awb_stats), 106*6edb685aSTomi Valkeinen sizeof(struct pisp_fe_awb_stats_config) }, 107*6edb685aSTomi Valkeinen { PISP_FE_ENABLE_RGBY, 0, 108*6edb685aSTomi Valkeinen offsetof(struct pisp_fe_config, rgby), 109*6edb685aSTomi Valkeinen sizeof(struct pisp_fe_rgby_config) }, 110*6edb685aSTomi Valkeinen { PISP_FE_ENABLE_LSC, 0, 111*6edb685aSTomi Valkeinen offsetof(struct pisp_fe_config, lsc), 112*6edb685aSTomi Valkeinen sizeof(struct pisp_fe_lsc_config) }, 113*6edb685aSTomi Valkeinen { PISP_FE_ENABLE_AGC_STATS, 0, 114*6edb685aSTomi Valkeinen offsetof(struct pisp_fe_config, agc_stats), 115*6edb685aSTomi Valkeinen sizeof(struct pisp_agc_statistics) }, 116*6edb685aSTomi Valkeinen { PISP_FE_ENABLE_CROP0, 0, 117*6edb685aSTomi Valkeinen offsetof(struct pisp_fe_config, ch[0].crop), 118*6edb685aSTomi Valkeinen sizeof(struct pisp_fe_crop_config) }, 119*6edb685aSTomi Valkeinen { PISP_FE_ENABLE_DOWNSCALE0, 0, 120*6edb685aSTomi Valkeinen offsetof(struct pisp_fe_config, ch[0].downscale), 121*6edb685aSTomi Valkeinen sizeof(struct pisp_fe_downscale_config) }, 122*6edb685aSTomi Valkeinen { PISP_FE_ENABLE_COMPRESS0, 0, 123*6edb685aSTomi Valkeinen offsetof(struct pisp_fe_config, ch[0].compress), 124*6edb685aSTomi Valkeinen sizeof(struct pisp_compress_config) }, 125*6edb685aSTomi Valkeinen { PISP_FE_ENABLE_OUTPUT0, 0, 126*6edb685aSTomi Valkeinen offsetof(struct pisp_fe_config, ch[0].output), 127*6edb685aSTomi Valkeinen sizeof(struct pisp_fe_output_config) }, 128*6edb685aSTomi Valkeinen { PISP_FE_ENABLE_CROP1, 0, 129*6edb685aSTomi Valkeinen offsetof(struct pisp_fe_config, ch[1].crop), 130*6edb685aSTomi Valkeinen sizeof(struct pisp_fe_crop_config) }, 131*6edb685aSTomi Valkeinen { PISP_FE_ENABLE_DOWNSCALE1, 0, 132*6edb685aSTomi Valkeinen offsetof(struct pisp_fe_config, ch[1].downscale), 133*6edb685aSTomi Valkeinen sizeof(struct pisp_fe_downscale_config) }, 134*6edb685aSTomi Valkeinen { PISP_FE_ENABLE_COMPRESS1, 0, 135*6edb685aSTomi Valkeinen offsetof(struct pisp_fe_config, ch[1].compress), 136*6edb685aSTomi Valkeinen sizeof(struct pisp_compress_config) }, 137*6edb685aSTomi Valkeinen { PISP_FE_ENABLE_OUTPUT1, 0, 138*6edb685aSTomi Valkeinen offsetof(struct pisp_fe_config, ch[1].output), 139*6edb685aSTomi Valkeinen sizeof(struct pisp_fe_output_config) }, 140*6edb685aSTomi Valkeinen }; 141*6edb685aSTomi Valkeinen 142*6edb685aSTomi Valkeinen #define pisp_fe_dbg(fe, fmt, arg...) dev_dbg((fe)->v4l2_dev->dev, fmt, ##arg) 143*6edb685aSTomi Valkeinen #define pisp_fe_info(fe, fmt, arg...) dev_info((fe)->v4l2_dev->dev, fmt, ##arg) 144*6edb685aSTomi Valkeinen #define pisp_fe_err(fe, fmt, arg...) dev_err((fe)->v4l2_dev->dev, fmt, ##arg) 145*6edb685aSTomi Valkeinen 146*6edb685aSTomi Valkeinen static inline u32 pisp_fe_reg_read(struct pisp_fe_device *fe, u32 offset) 147*6edb685aSTomi Valkeinen { 148*6edb685aSTomi Valkeinen return readl(fe->base + offset); 149*6edb685aSTomi Valkeinen } 150*6edb685aSTomi Valkeinen 151*6edb685aSTomi Valkeinen static inline void pisp_fe_reg_write(struct pisp_fe_device *fe, u32 offset, 152*6edb685aSTomi Valkeinen u32 val) 153*6edb685aSTomi Valkeinen { 154*6edb685aSTomi Valkeinen writel(val, fe->base + offset); 155*6edb685aSTomi Valkeinen } 156*6edb685aSTomi Valkeinen 157*6edb685aSTomi Valkeinen static inline void pisp_fe_reg_write_relaxed(struct pisp_fe_device *fe, 158*6edb685aSTomi Valkeinen u32 offset, u32 val) 159*6edb685aSTomi Valkeinen { 160*6edb685aSTomi Valkeinen writel_relaxed(val, fe->base + offset); 161*6edb685aSTomi Valkeinen } 162*6edb685aSTomi Valkeinen 163*6edb685aSTomi Valkeinen static int pisp_fe_regs_show(struct seq_file *s, void *data) 164*6edb685aSTomi Valkeinen { 165*6edb685aSTomi Valkeinen struct pisp_fe_device *fe = s->private; 166*6edb685aSTomi Valkeinen int ret; 167*6edb685aSTomi Valkeinen 168*6edb685aSTomi Valkeinen ret = pm_runtime_resume_and_get(fe->v4l2_dev->dev); 169*6edb685aSTomi Valkeinen if (ret) 170*6edb685aSTomi Valkeinen return ret; 171*6edb685aSTomi Valkeinen 172*6edb685aSTomi Valkeinen pisp_fe_reg_write(fe, FE_CONTROL, FE_CONTROL_LATCH_REGS); 173*6edb685aSTomi Valkeinen 174*6edb685aSTomi Valkeinen #define DUMP(reg) seq_printf(s, #reg " \t0x%08x\n", pisp_fe_reg_read(fe, reg)) 175*6edb685aSTomi Valkeinen DUMP(FE_VERSION); 176*6edb685aSTomi Valkeinen DUMP(FE_CONTROL); 177*6edb685aSTomi Valkeinen DUMP(FE_STATUS); 178*6edb685aSTomi Valkeinen DUMP(FE_FRAME_STATUS); 179*6edb685aSTomi Valkeinen DUMP(FE_ERROR_STATUS); 180*6edb685aSTomi Valkeinen DUMP(FE_OUTPUT_STATUS); 181*6edb685aSTomi Valkeinen DUMP(FE_INT_EN); 182*6edb685aSTomi Valkeinen DUMP(FE_INT_STATUS); 183*6edb685aSTomi Valkeinen #undef DUMP 184*6edb685aSTomi Valkeinen 185*6edb685aSTomi Valkeinen pm_runtime_put(fe->v4l2_dev->dev); 186*6edb685aSTomi Valkeinen 187*6edb685aSTomi Valkeinen return 0; 188*6edb685aSTomi Valkeinen } 189*6edb685aSTomi Valkeinen 190*6edb685aSTomi Valkeinen DEFINE_SHOW_ATTRIBUTE(pisp_fe_regs); 191*6edb685aSTomi Valkeinen 192*6edb685aSTomi Valkeinen static void pisp_fe_config_write(struct pisp_fe_device *fe, 193*6edb685aSTomi Valkeinen struct pisp_fe_config *config, 194*6edb685aSTomi Valkeinen unsigned int start_offset, unsigned int size) 195*6edb685aSTomi Valkeinen { 196*6edb685aSTomi Valkeinen const unsigned int max_offset = 197*6edb685aSTomi Valkeinen offsetof(struct pisp_fe_config, ch[PISP_FE_NUM_OUTPUTS]); 198*6edb685aSTomi Valkeinen unsigned int end_offset; 199*6edb685aSTomi Valkeinen u32 *cfg = (u32 *)config; 200*6edb685aSTomi Valkeinen 201*6edb685aSTomi Valkeinen start_offset = min(start_offset, max_offset); 202*6edb685aSTomi Valkeinen end_offset = min(start_offset + size, max_offset); 203*6edb685aSTomi Valkeinen 204*6edb685aSTomi Valkeinen cfg += start_offset >> 2; 205*6edb685aSTomi Valkeinen for (unsigned int i = start_offset; i < end_offset; i += 4, cfg++) 206*6edb685aSTomi Valkeinen pisp_fe_reg_write_relaxed(fe, PISP_FE_CONFIG_BASE_OFFSET + i, 207*6edb685aSTomi Valkeinen *cfg); 208*6edb685aSTomi Valkeinen } 209*6edb685aSTomi Valkeinen 210*6edb685aSTomi Valkeinen void pisp_fe_isr(struct pisp_fe_device *fe, bool *sof, bool *eof) 211*6edb685aSTomi Valkeinen { 212*6edb685aSTomi Valkeinen u32 status, int_status, out_status, frame_status, error_status; 213*6edb685aSTomi Valkeinen 214*6edb685aSTomi Valkeinen pisp_fe_reg_write(fe, FE_CONTROL, FE_CONTROL_LATCH_REGS); 215*6edb685aSTomi Valkeinen status = pisp_fe_reg_read(fe, FE_STATUS); 216*6edb685aSTomi Valkeinen out_status = pisp_fe_reg_read(fe, FE_OUTPUT_STATUS); 217*6edb685aSTomi Valkeinen frame_status = pisp_fe_reg_read(fe, FE_FRAME_STATUS); 218*6edb685aSTomi Valkeinen error_status = pisp_fe_reg_read(fe, FE_ERROR_STATUS); 219*6edb685aSTomi Valkeinen 220*6edb685aSTomi Valkeinen int_status = pisp_fe_reg_read(fe, FE_INT_STATUS); 221*6edb685aSTomi Valkeinen pisp_fe_reg_write(fe, FE_INT_STATUS, int_status); 222*6edb685aSTomi Valkeinen 223*6edb685aSTomi Valkeinen trace_fe_irq(status, out_status, frame_status, error_status, 224*6edb685aSTomi Valkeinen int_status); 225*6edb685aSTomi Valkeinen 226*6edb685aSTomi Valkeinen /* We do not report interrupts for the input/stream pad. */ 227*6edb685aSTomi Valkeinen for (unsigned int i = 0; i < FE_NUM_PADS - 1; i++) { 228*6edb685aSTomi Valkeinen sof[i] = !!(int_status & FE_INT_SOF); 229*6edb685aSTomi Valkeinen eof[i] = !!(int_status & FE_INT_EOF); 230*6edb685aSTomi Valkeinen } 231*6edb685aSTomi Valkeinen } 232*6edb685aSTomi Valkeinen 233*6edb685aSTomi Valkeinen static bool pisp_fe_validate_output(struct pisp_fe_config const *cfg, 234*6edb685aSTomi Valkeinen unsigned int c, struct v4l2_format const *f) 235*6edb685aSTomi Valkeinen { 236*6edb685aSTomi Valkeinen unsigned int wbytes; 237*6edb685aSTomi Valkeinen 238*6edb685aSTomi Valkeinen wbytes = cfg->ch[c].output.format.width; 239*6edb685aSTomi Valkeinen if (cfg->ch[c].output.format.format & PISP_IMAGE_FORMAT_BPS_MASK) 240*6edb685aSTomi Valkeinen wbytes *= 2; 241*6edb685aSTomi Valkeinen 242*6edb685aSTomi Valkeinen /* Check output image dimensions are nonzero and not too big */ 243*6edb685aSTomi Valkeinen if (cfg->ch[c].output.format.width < 2 || 244*6edb685aSTomi Valkeinen cfg->ch[c].output.format.height < 2 || 245*6edb685aSTomi Valkeinen cfg->ch[c].output.format.height > f->fmt.pix.height || 246*6edb685aSTomi Valkeinen cfg->ch[c].output.format.stride > f->fmt.pix.bytesperline || 247*6edb685aSTomi Valkeinen wbytes > f->fmt.pix.bytesperline) 248*6edb685aSTomi Valkeinen return false; 249*6edb685aSTomi Valkeinen 250*6edb685aSTomi Valkeinen /* Check for zero-sized crops, which could cause lockup */ 251*6edb685aSTomi Valkeinen if ((cfg->global.enables & PISP_FE_ENABLE_CROP(c)) && 252*6edb685aSTomi Valkeinen ((cfg->ch[c].crop.offset_x >= (cfg->input.format.width & ~1) || 253*6edb685aSTomi Valkeinen cfg->ch[c].crop.offset_y >= cfg->input.format.height || 254*6edb685aSTomi Valkeinen cfg->ch[c].crop.width < 2 || cfg->ch[c].crop.height < 2))) 255*6edb685aSTomi Valkeinen return false; 256*6edb685aSTomi Valkeinen 257*6edb685aSTomi Valkeinen if ((cfg->global.enables & PISP_FE_ENABLE_DOWNSCALE(c)) && 258*6edb685aSTomi Valkeinen (cfg->ch[c].downscale.output_width < 2 || 259*6edb685aSTomi Valkeinen cfg->ch[c].downscale.output_height < 2)) 260*6edb685aSTomi Valkeinen return false; 261*6edb685aSTomi Valkeinen 262*6edb685aSTomi Valkeinen return true; 263*6edb685aSTomi Valkeinen } 264*6edb685aSTomi Valkeinen 265*6edb685aSTomi Valkeinen static bool pisp_fe_validate_stats(struct pisp_fe_config const *cfg) 266*6edb685aSTomi Valkeinen { 267*6edb685aSTomi Valkeinen /* Check for zero-sized crop, which could cause lockup */ 268*6edb685aSTomi Valkeinen return (!(cfg->global.enables & PISP_FE_ENABLE_STATS_CROP) || 269*6edb685aSTomi Valkeinen (cfg->stats_crop.offset_x < (cfg->input.format.width & ~1) && 270*6edb685aSTomi Valkeinen cfg->stats_crop.offset_y < cfg->input.format.height && 271*6edb685aSTomi Valkeinen cfg->stats_crop.width >= 2 && cfg->stats_crop.height >= 2)); 272*6edb685aSTomi Valkeinen } 273*6edb685aSTomi Valkeinen 274*6edb685aSTomi Valkeinen int pisp_fe_validate_config(struct pisp_fe_device *fe, 275*6edb685aSTomi Valkeinen struct pisp_fe_config *cfg, 276*6edb685aSTomi Valkeinen struct v4l2_format const *f0, 277*6edb685aSTomi Valkeinen struct v4l2_format const *f1) 278*6edb685aSTomi Valkeinen { 279*6edb685aSTomi Valkeinen /* 280*6edb685aSTomi Valkeinen * Check the input is enabled, streaming and has nonzero size; 281*6edb685aSTomi Valkeinen * to avoid cases where the hardware might lock up or try to 282*6edb685aSTomi Valkeinen * read inputs from memory (which this driver doesn't support). 283*6edb685aSTomi Valkeinen */ 284*6edb685aSTomi Valkeinen if (!(cfg->global.enables & PISP_FE_ENABLE_INPUT) || 285*6edb685aSTomi Valkeinen cfg->input.streaming != 1 || cfg->input.format.width < 2 || 286*6edb685aSTomi Valkeinen cfg->input.format.height < 2) { 287*6edb685aSTomi Valkeinen pisp_fe_err(fe, "%s: Input config not valid", __func__); 288*6edb685aSTomi Valkeinen return -EINVAL; 289*6edb685aSTomi Valkeinen } 290*6edb685aSTomi Valkeinen 291*6edb685aSTomi Valkeinen for (unsigned int i = 0; i < PISP_FE_NUM_OUTPUTS; i++) { 292*6edb685aSTomi Valkeinen if (!(cfg->global.enables & PISP_FE_ENABLE_OUTPUT(i))) { 293*6edb685aSTomi Valkeinen if (cfg->global.enables & 294*6edb685aSTomi Valkeinen PISP_FE_ENABLE_OUTPUT_CLUSTER(i)) { 295*6edb685aSTomi Valkeinen pisp_fe_err(fe, "%s: Output %u not valid", 296*6edb685aSTomi Valkeinen __func__, i); 297*6edb685aSTomi Valkeinen return -EINVAL; 298*6edb685aSTomi Valkeinen } 299*6edb685aSTomi Valkeinen continue; 300*6edb685aSTomi Valkeinen } 301*6edb685aSTomi Valkeinen 302*6edb685aSTomi Valkeinen if (!pisp_fe_validate_output(cfg, i, i ? f1 : f0)) 303*6edb685aSTomi Valkeinen return -EINVAL; 304*6edb685aSTomi Valkeinen } 305*6edb685aSTomi Valkeinen 306*6edb685aSTomi Valkeinen if ((cfg->global.enables & PISP_FE_ENABLE_STATS_CLUSTER) && 307*6edb685aSTomi Valkeinen !pisp_fe_validate_stats(cfg)) { 308*6edb685aSTomi Valkeinen pisp_fe_err(fe, "%s: Stats config not valid", __func__); 309*6edb685aSTomi Valkeinen return -EINVAL; 310*6edb685aSTomi Valkeinen } 311*6edb685aSTomi Valkeinen 312*6edb685aSTomi Valkeinen return 0; 313*6edb685aSTomi Valkeinen } 314*6edb685aSTomi Valkeinen 315*6edb685aSTomi Valkeinen void pisp_fe_submit_job(struct pisp_fe_device *fe, struct vb2_buffer **vb2_bufs, 316*6edb685aSTomi Valkeinen struct pisp_fe_config *cfg) 317*6edb685aSTomi Valkeinen { 318*6edb685aSTomi Valkeinen u64 addr; 319*6edb685aSTomi Valkeinen u32 status; 320*6edb685aSTomi Valkeinen 321*6edb685aSTomi Valkeinen /* 322*6edb685aSTomi Valkeinen * Check output buffers exist and outputs are correctly configured. 323*6edb685aSTomi Valkeinen * If valid, set the buffer's DMA address; otherwise disable. 324*6edb685aSTomi Valkeinen */ 325*6edb685aSTomi Valkeinen for (unsigned int i = 0; i < PISP_FE_NUM_OUTPUTS; i++) { 326*6edb685aSTomi Valkeinen struct vb2_buffer *buf = vb2_bufs[FE_OUTPUT0_PAD + i]; 327*6edb685aSTomi Valkeinen 328*6edb685aSTomi Valkeinen if (!(cfg->global.enables & PISP_FE_ENABLE_OUTPUT(i))) 329*6edb685aSTomi Valkeinen continue; 330*6edb685aSTomi Valkeinen 331*6edb685aSTomi Valkeinen addr = vb2_dma_contig_plane_dma_addr(buf, 0); 332*6edb685aSTomi Valkeinen cfg->output_buffer[i].addr_lo = addr & 0xffffffff; 333*6edb685aSTomi Valkeinen cfg->output_buffer[i].addr_hi = addr >> 32; 334*6edb685aSTomi Valkeinen } 335*6edb685aSTomi Valkeinen 336*6edb685aSTomi Valkeinen if (vb2_bufs[FE_STATS_PAD]) { 337*6edb685aSTomi Valkeinen addr = vb2_dma_contig_plane_dma_addr(vb2_bufs[FE_STATS_PAD], 0); 338*6edb685aSTomi Valkeinen cfg->stats_buffer.addr_lo = addr & 0xffffffff; 339*6edb685aSTomi Valkeinen cfg->stats_buffer.addr_hi = addr >> 32; 340*6edb685aSTomi Valkeinen } 341*6edb685aSTomi Valkeinen 342*6edb685aSTomi Valkeinen /* Set up ILINES interrupts 3/4 of the way down each output */ 343*6edb685aSTomi Valkeinen cfg->ch[0].output.ilines = 344*6edb685aSTomi Valkeinen max(0x80u, (3u * cfg->ch[0].output.format.height) >> 2); 345*6edb685aSTomi Valkeinen cfg->ch[1].output.ilines = 346*6edb685aSTomi Valkeinen max(0x80u, (3u * cfg->ch[1].output.format.height) >> 2); 347*6edb685aSTomi Valkeinen 348*6edb685aSTomi Valkeinen /* 349*6edb685aSTomi Valkeinen * The hardware must have consumed the previous config by now. 350*6edb685aSTomi Valkeinen * This read of status also serves as a memory barrier before the 351*6edb685aSTomi Valkeinen * sequence of relaxed writes which follow. 352*6edb685aSTomi Valkeinen */ 353*6edb685aSTomi Valkeinen status = pisp_fe_reg_read(fe, FE_STATUS); 354*6edb685aSTomi Valkeinen if (WARN_ON(status & FE_STATUS_QUEUED)) 355*6edb685aSTomi Valkeinen return; 356*6edb685aSTomi Valkeinen 357*6edb685aSTomi Valkeinen /* 358*6edb685aSTomi Valkeinen * Unconditionally write buffers, global and input parameters. 359*6edb685aSTomi Valkeinen * Write cropping and output parameters whenever they are enabled. 360*6edb685aSTomi Valkeinen * Selectively write other parameters that have been marked as 361*6edb685aSTomi Valkeinen * changed through the dirty flags. 362*6edb685aSTomi Valkeinen */ 363*6edb685aSTomi Valkeinen pisp_fe_config_write(fe, cfg, 0, 364*6edb685aSTomi Valkeinen offsetof(struct pisp_fe_config, decompress)); 365*6edb685aSTomi Valkeinen cfg->dirty_flags_extra &= ~PISP_FE_DIRTY_GLOBAL; 366*6edb685aSTomi Valkeinen cfg->dirty_flags &= ~PISP_FE_ENABLE_INPUT; 367*6edb685aSTomi Valkeinen cfg->dirty_flags |= (cfg->global.enables & 368*6edb685aSTomi Valkeinen (PISP_FE_ENABLE_STATS_CROP | 369*6edb685aSTomi Valkeinen PISP_FE_ENABLE_OUTPUT_CLUSTER(0) | 370*6edb685aSTomi Valkeinen PISP_FE_ENABLE_OUTPUT_CLUSTER(1))); 371*6edb685aSTomi Valkeinen for (unsigned int i = 0; i < ARRAY_SIZE(pisp_fe_config_map); i++) { 372*6edb685aSTomi Valkeinen const struct pisp_fe_config_param *p = &pisp_fe_config_map[i]; 373*6edb685aSTomi Valkeinen 374*6edb685aSTomi Valkeinen if (cfg->dirty_flags & p->dirty_flags || 375*6edb685aSTomi Valkeinen cfg->dirty_flags_extra & p->dirty_flags_extra) 376*6edb685aSTomi Valkeinen pisp_fe_config_write(fe, cfg, p->offset, p->size); 377*6edb685aSTomi Valkeinen } 378*6edb685aSTomi Valkeinen 379*6edb685aSTomi Valkeinen /* This final non-relaxed write serves as a memory barrier */ 380*6edb685aSTomi Valkeinen pisp_fe_reg_write(fe, FE_CONTROL, FE_CONTROL_QUEUE); 381*6edb685aSTomi Valkeinen } 382*6edb685aSTomi Valkeinen 383*6edb685aSTomi Valkeinen void pisp_fe_start(struct pisp_fe_device *fe) 384*6edb685aSTomi Valkeinen { 385*6edb685aSTomi Valkeinen pisp_fe_reg_write(fe, FE_CONTROL, FE_CONTROL_RESET); 386*6edb685aSTomi Valkeinen pisp_fe_reg_write(fe, FE_INT_STATUS, ~0); 387*6edb685aSTomi Valkeinen pisp_fe_reg_write(fe, FE_INT_EN, FE_INT_EOF | FE_INT_SOF | 388*6edb685aSTomi Valkeinen FE_INT_LINES0 | FE_INT_LINES1); 389*6edb685aSTomi Valkeinen fe->inframe_count = 0; 390*6edb685aSTomi Valkeinen } 391*6edb685aSTomi Valkeinen 392*6edb685aSTomi Valkeinen void pisp_fe_stop(struct pisp_fe_device *fe) 393*6edb685aSTomi Valkeinen { 394*6edb685aSTomi Valkeinen pisp_fe_reg_write(fe, FE_INT_EN, 0); 395*6edb685aSTomi Valkeinen pisp_fe_reg_write(fe, FE_CONTROL, FE_CONTROL_ABORT); 396*6edb685aSTomi Valkeinen usleep_range(1000, 2000); 397*6edb685aSTomi Valkeinen WARN_ON(pisp_fe_reg_read(fe, FE_STATUS)); 398*6edb685aSTomi Valkeinen pisp_fe_reg_write(fe, FE_INT_STATUS, ~0); 399*6edb685aSTomi Valkeinen } 400*6edb685aSTomi Valkeinen 401*6edb685aSTomi Valkeinen static int pisp_fe_init_state(struct v4l2_subdev *sd, 402*6edb685aSTomi Valkeinen struct v4l2_subdev_state *state) 403*6edb685aSTomi Valkeinen { 404*6edb685aSTomi Valkeinen struct v4l2_mbus_framefmt *fmt; 405*6edb685aSTomi Valkeinen 406*6edb685aSTomi Valkeinen fmt = v4l2_subdev_state_get_format(state, FE_STREAM_PAD); 407*6edb685aSTomi Valkeinen *fmt = cfe_default_format; 408*6edb685aSTomi Valkeinen fmt->code = MEDIA_BUS_FMT_SRGGB16_1X16; 409*6edb685aSTomi Valkeinen 410*6edb685aSTomi Valkeinen fmt = v4l2_subdev_state_get_format(state, FE_CONFIG_PAD); 411*6edb685aSTomi Valkeinen fmt->code = MEDIA_BUS_FMT_FIXED; 412*6edb685aSTomi Valkeinen fmt->width = sizeof(struct pisp_fe_config); 413*6edb685aSTomi Valkeinen fmt->height = 1; 414*6edb685aSTomi Valkeinen 415*6edb685aSTomi Valkeinen fmt = v4l2_subdev_state_get_format(state, FE_OUTPUT0_PAD); 416*6edb685aSTomi Valkeinen *fmt = cfe_default_format; 417*6edb685aSTomi Valkeinen fmt->code = MEDIA_BUS_FMT_SRGGB16_1X16; 418*6edb685aSTomi Valkeinen 419*6edb685aSTomi Valkeinen fmt = v4l2_subdev_state_get_format(state, FE_OUTPUT1_PAD); 420*6edb685aSTomi Valkeinen *fmt = cfe_default_format; 421*6edb685aSTomi Valkeinen fmt->code = MEDIA_BUS_FMT_SRGGB16_1X16; 422*6edb685aSTomi Valkeinen 423*6edb685aSTomi Valkeinen fmt = v4l2_subdev_state_get_format(state, FE_STATS_PAD); 424*6edb685aSTomi Valkeinen fmt->code = MEDIA_BUS_FMT_FIXED; 425*6edb685aSTomi Valkeinen fmt->width = sizeof(struct pisp_statistics); 426*6edb685aSTomi Valkeinen fmt->height = 1; 427*6edb685aSTomi Valkeinen 428*6edb685aSTomi Valkeinen return 0; 429*6edb685aSTomi Valkeinen } 430*6edb685aSTomi Valkeinen 431*6edb685aSTomi Valkeinen static int pisp_fe_pad_set_fmt(struct v4l2_subdev *sd, 432*6edb685aSTomi Valkeinen struct v4l2_subdev_state *state, 433*6edb685aSTomi Valkeinen struct v4l2_subdev_format *format) 434*6edb685aSTomi Valkeinen { 435*6edb685aSTomi Valkeinen struct v4l2_mbus_framefmt *fmt; 436*6edb685aSTomi Valkeinen const struct cfe_fmt *cfe_fmt; 437*6edb685aSTomi Valkeinen 438*6edb685aSTomi Valkeinen /* TODO: format propagation to source pads */ 439*6edb685aSTomi Valkeinen /* TODO: format validation */ 440*6edb685aSTomi Valkeinen 441*6edb685aSTomi Valkeinen switch (format->pad) { 442*6edb685aSTomi Valkeinen case FE_STREAM_PAD: 443*6edb685aSTomi Valkeinen cfe_fmt = find_format_by_code(format->format.code); 444*6edb685aSTomi Valkeinen if (!cfe_fmt || !(cfe_fmt->flags & CFE_FORMAT_FLAG_FE_OUT)) 445*6edb685aSTomi Valkeinen cfe_fmt = find_format_by_code(MEDIA_BUS_FMT_SRGGB16_1X16); 446*6edb685aSTomi Valkeinen 447*6edb685aSTomi Valkeinen format->format.code = cfe_fmt->code; 448*6edb685aSTomi Valkeinen format->format.field = V4L2_FIELD_NONE; 449*6edb685aSTomi Valkeinen 450*6edb685aSTomi Valkeinen fmt = v4l2_subdev_state_get_format(state, FE_STREAM_PAD); 451*6edb685aSTomi Valkeinen *fmt = format->format; 452*6edb685aSTomi Valkeinen 453*6edb685aSTomi Valkeinen fmt = v4l2_subdev_state_get_format(state, FE_OUTPUT0_PAD); 454*6edb685aSTomi Valkeinen *fmt = format->format; 455*6edb685aSTomi Valkeinen 456*6edb685aSTomi Valkeinen fmt = v4l2_subdev_state_get_format(state, FE_OUTPUT1_PAD); 457*6edb685aSTomi Valkeinen *fmt = format->format; 458*6edb685aSTomi Valkeinen 459*6edb685aSTomi Valkeinen return 0; 460*6edb685aSTomi Valkeinen 461*6edb685aSTomi Valkeinen case FE_OUTPUT0_PAD: 462*6edb685aSTomi Valkeinen case FE_OUTPUT1_PAD: { 463*6edb685aSTomi Valkeinen /* 464*6edb685aSTomi Valkeinen * TODO: we should allow scaling and cropping by allowing the 465*6edb685aSTomi Valkeinen * user to set the size here. 466*6edb685aSTomi Valkeinen */ 467*6edb685aSTomi Valkeinen struct v4l2_mbus_framefmt *sink_fmt, *source_fmt; 468*6edb685aSTomi Valkeinen u32 sink_code; 469*6edb685aSTomi Valkeinen u32 code; 470*6edb685aSTomi Valkeinen 471*6edb685aSTomi Valkeinen cfe_fmt = find_format_by_code(format->format.code); 472*6edb685aSTomi Valkeinen if (!cfe_fmt || !(cfe_fmt->flags & CFE_FORMAT_FLAG_FE_OUT)) 473*6edb685aSTomi Valkeinen cfe_fmt = find_format_by_code(MEDIA_BUS_FMT_SRGGB16_1X16); 474*6edb685aSTomi Valkeinen 475*6edb685aSTomi Valkeinen format->format.code = cfe_fmt->code; 476*6edb685aSTomi Valkeinen 477*6edb685aSTomi Valkeinen sink_fmt = v4l2_subdev_state_get_format(state, FE_STREAM_PAD); 478*6edb685aSTomi Valkeinen if (!sink_fmt) 479*6edb685aSTomi Valkeinen return -EINVAL; 480*6edb685aSTomi Valkeinen 481*6edb685aSTomi Valkeinen source_fmt = v4l2_subdev_state_get_format(state, format->pad); 482*6edb685aSTomi Valkeinen if (!source_fmt) 483*6edb685aSTomi Valkeinen return -EINVAL; 484*6edb685aSTomi Valkeinen 485*6edb685aSTomi Valkeinen sink_code = sink_fmt->code; 486*6edb685aSTomi Valkeinen code = format->format.code; 487*6edb685aSTomi Valkeinen 488*6edb685aSTomi Valkeinen /* 489*6edb685aSTomi Valkeinen * If the source code from the user does not match the code in 490*6edb685aSTomi Valkeinen * the sink pad, check that the source code matches the 491*6edb685aSTomi Valkeinen * compressed version of the sink code. 492*6edb685aSTomi Valkeinen */ 493*6edb685aSTomi Valkeinen 494*6edb685aSTomi Valkeinen if (code != sink_code && 495*6edb685aSTomi Valkeinen code == cfe_find_compressed_code(sink_code)) 496*6edb685aSTomi Valkeinen source_fmt->code = code; 497*6edb685aSTomi Valkeinen 498*6edb685aSTomi Valkeinen return 0; 499*6edb685aSTomi Valkeinen } 500*6edb685aSTomi Valkeinen 501*6edb685aSTomi Valkeinen case FE_CONFIG_PAD: 502*6edb685aSTomi Valkeinen case FE_STATS_PAD: 503*6edb685aSTomi Valkeinen default: 504*6edb685aSTomi Valkeinen return v4l2_subdev_get_fmt(sd, state, format); 505*6edb685aSTomi Valkeinen } 506*6edb685aSTomi Valkeinen } 507*6edb685aSTomi Valkeinen 508*6edb685aSTomi Valkeinen static const struct v4l2_subdev_pad_ops pisp_fe_subdev_pad_ops = { 509*6edb685aSTomi Valkeinen .get_fmt = v4l2_subdev_get_fmt, 510*6edb685aSTomi Valkeinen .set_fmt = pisp_fe_pad_set_fmt, 511*6edb685aSTomi Valkeinen .link_validate = v4l2_subdev_link_validate_default, 512*6edb685aSTomi Valkeinen }; 513*6edb685aSTomi Valkeinen 514*6edb685aSTomi Valkeinen static int pisp_fe_link_validate(struct media_link *link) 515*6edb685aSTomi Valkeinen { 516*6edb685aSTomi Valkeinen struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(link->sink->entity); 517*6edb685aSTomi Valkeinen struct pisp_fe_device *fe = container_of(sd, struct pisp_fe_device, sd); 518*6edb685aSTomi Valkeinen 519*6edb685aSTomi Valkeinen pisp_fe_dbg(fe, "%s: link \"%s\":%u -> \"%s\":%u\n", __func__, 520*6edb685aSTomi Valkeinen link->source->entity->name, link->source->index, 521*6edb685aSTomi Valkeinen link->sink->entity->name, link->sink->index); 522*6edb685aSTomi Valkeinen 523*6edb685aSTomi Valkeinen if (link->sink->index == FE_STREAM_PAD) 524*6edb685aSTomi Valkeinen return v4l2_subdev_link_validate(link); 525*6edb685aSTomi Valkeinen 526*6edb685aSTomi Valkeinen if (link->sink->index == FE_CONFIG_PAD) 527*6edb685aSTomi Valkeinen return 0; 528*6edb685aSTomi Valkeinen 529*6edb685aSTomi Valkeinen return -EINVAL; 530*6edb685aSTomi Valkeinen } 531*6edb685aSTomi Valkeinen 532*6edb685aSTomi Valkeinen static const struct media_entity_operations pisp_fe_entity_ops = { 533*6edb685aSTomi Valkeinen .link_validate = pisp_fe_link_validate, 534*6edb685aSTomi Valkeinen }; 535*6edb685aSTomi Valkeinen 536*6edb685aSTomi Valkeinen static const struct v4l2_subdev_ops pisp_fe_subdev_ops = { 537*6edb685aSTomi Valkeinen .pad = &pisp_fe_subdev_pad_ops, 538*6edb685aSTomi Valkeinen }; 539*6edb685aSTomi Valkeinen 540*6edb685aSTomi Valkeinen static const struct v4l2_subdev_internal_ops pisp_fe_internal_ops = { 541*6edb685aSTomi Valkeinen .init_state = pisp_fe_init_state, 542*6edb685aSTomi Valkeinen }; 543*6edb685aSTomi Valkeinen 544*6edb685aSTomi Valkeinen int pisp_fe_init(struct pisp_fe_device *fe, struct dentry *debugfs) 545*6edb685aSTomi Valkeinen { 546*6edb685aSTomi Valkeinen int ret; 547*6edb685aSTomi Valkeinen 548*6edb685aSTomi Valkeinen debugfs_create_file("fe_regs", 0440, debugfs, fe, &pisp_fe_regs_fops); 549*6edb685aSTomi Valkeinen 550*6edb685aSTomi Valkeinen fe->hw_revision = pisp_fe_reg_read(fe, FE_VERSION); 551*6edb685aSTomi Valkeinen pisp_fe_info(fe, "PiSP FE HW v%u.%u\n", 552*6edb685aSTomi Valkeinen (fe->hw_revision >> 24) & 0xff, 553*6edb685aSTomi Valkeinen (fe->hw_revision >> 20) & 0x0f); 554*6edb685aSTomi Valkeinen 555*6edb685aSTomi Valkeinen fe->pad[FE_STREAM_PAD].flags = 556*6edb685aSTomi Valkeinen MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT; 557*6edb685aSTomi Valkeinen fe->pad[FE_CONFIG_PAD].flags = MEDIA_PAD_FL_SINK; 558*6edb685aSTomi Valkeinen fe->pad[FE_OUTPUT0_PAD].flags = MEDIA_PAD_FL_SOURCE; 559*6edb685aSTomi Valkeinen fe->pad[FE_OUTPUT1_PAD].flags = MEDIA_PAD_FL_SOURCE; 560*6edb685aSTomi Valkeinen fe->pad[FE_STATS_PAD].flags = MEDIA_PAD_FL_SOURCE; 561*6edb685aSTomi Valkeinen 562*6edb685aSTomi Valkeinen ret = media_entity_pads_init(&fe->sd.entity, ARRAY_SIZE(fe->pad), 563*6edb685aSTomi Valkeinen fe->pad); 564*6edb685aSTomi Valkeinen if (ret) 565*6edb685aSTomi Valkeinen return ret; 566*6edb685aSTomi Valkeinen 567*6edb685aSTomi Valkeinen /* Initialize subdev */ 568*6edb685aSTomi Valkeinen v4l2_subdev_init(&fe->sd, &pisp_fe_subdev_ops); 569*6edb685aSTomi Valkeinen fe->sd.internal_ops = &pisp_fe_internal_ops; 570*6edb685aSTomi Valkeinen fe->sd.entity.function = MEDIA_ENT_F_PROC_VIDEO_SCALER; 571*6edb685aSTomi Valkeinen fe->sd.entity.ops = &pisp_fe_entity_ops; 572*6edb685aSTomi Valkeinen fe->sd.entity.name = "pisp-fe"; 573*6edb685aSTomi Valkeinen fe->sd.flags = V4L2_SUBDEV_FL_HAS_DEVNODE; 574*6edb685aSTomi Valkeinen fe->sd.owner = THIS_MODULE; 575*6edb685aSTomi Valkeinen snprintf(fe->sd.name, sizeof(fe->sd.name), "pisp-fe"); 576*6edb685aSTomi Valkeinen 577*6edb685aSTomi Valkeinen ret = v4l2_subdev_init_finalize(&fe->sd); 578*6edb685aSTomi Valkeinen if (ret) 579*6edb685aSTomi Valkeinen goto err_entity_cleanup; 580*6edb685aSTomi Valkeinen 581*6edb685aSTomi Valkeinen ret = v4l2_device_register_subdev(fe->v4l2_dev, &fe->sd); 582*6edb685aSTomi Valkeinen if (ret) { 583*6edb685aSTomi Valkeinen pisp_fe_err(fe, "Failed register pisp fe subdev (%d)\n", ret); 584*6edb685aSTomi Valkeinen goto err_subdev_cleanup; 585*6edb685aSTomi Valkeinen } 586*6edb685aSTomi Valkeinen 587*6edb685aSTomi Valkeinen /* Must be in IDLE state (STATUS == 0) here. */ 588*6edb685aSTomi Valkeinen WARN_ON(pisp_fe_reg_read(fe, FE_STATUS)); 589*6edb685aSTomi Valkeinen 590*6edb685aSTomi Valkeinen return 0; 591*6edb685aSTomi Valkeinen 592*6edb685aSTomi Valkeinen err_subdev_cleanup: 593*6edb685aSTomi Valkeinen v4l2_subdev_cleanup(&fe->sd); 594*6edb685aSTomi Valkeinen err_entity_cleanup: 595*6edb685aSTomi Valkeinen media_entity_cleanup(&fe->sd.entity); 596*6edb685aSTomi Valkeinen 597*6edb685aSTomi Valkeinen return ret; 598*6edb685aSTomi Valkeinen } 599*6edb685aSTomi Valkeinen 600*6edb685aSTomi Valkeinen void pisp_fe_uninit(struct pisp_fe_device *fe) 601*6edb685aSTomi Valkeinen { 602*6edb685aSTomi Valkeinen v4l2_device_unregister_subdev(&fe->sd); 603*6edb685aSTomi Valkeinen v4l2_subdev_cleanup(&fe->sd); 604*6edb685aSTomi Valkeinen media_entity_cleanup(&fe->sd.entity); 605*6edb685aSTomi Valkeinen } 606