1 *6edb685aSTomi Valkeinen // SPDX-License-Identifier: GPL-2.0-only 2 *6edb685aSTomi Valkeinen /* 3 *6edb685aSTomi Valkeinen * RP1 CSI-2 Driver 4 *6edb685aSTomi Valkeinen * 5 *6edb685aSTomi Valkeinen * Copyright (c) 2021-2024 Raspberry Pi Ltd. 6 *6edb685aSTomi Valkeinen * Copyright (c) 2023-2024 Ideas on Board Oy 7 *6edb685aSTomi Valkeinen */ 8 *6edb685aSTomi Valkeinen 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 "csi2.h" 18 *6edb685aSTomi Valkeinen 19 *6edb685aSTomi Valkeinen #include "cfe-trace.h" 20 *6edb685aSTomi Valkeinen 21 *6edb685aSTomi Valkeinen static bool csi2_track_errors; 22 *6edb685aSTomi Valkeinen module_param_named(track_csi2_errors, csi2_track_errors, bool, 0); 23 *6edb685aSTomi Valkeinen MODULE_PARM_DESC(track_csi2_errors, "track csi-2 errors"); 24 *6edb685aSTomi Valkeinen 25 *6edb685aSTomi Valkeinen #define csi2_dbg(csi2, fmt, arg...) dev_dbg((csi2)->v4l2_dev->dev, fmt, ##arg) 26 *6edb685aSTomi Valkeinen #define csi2_err(csi2, fmt, arg...) dev_err((csi2)->v4l2_dev->dev, fmt, ##arg) 27 *6edb685aSTomi Valkeinen 28 *6edb685aSTomi Valkeinen /* CSI2-DMA registers */ 29 *6edb685aSTomi Valkeinen #define CSI2_STATUS 0x000 30 *6edb685aSTomi Valkeinen #define CSI2_QOS 0x004 31 *6edb685aSTomi Valkeinen #define CSI2_DISCARDS_OVERFLOW 0x008 32 *6edb685aSTomi Valkeinen #define CSI2_DISCARDS_INACTIVE 0x00c 33 *6edb685aSTomi Valkeinen #define CSI2_DISCARDS_UNMATCHED 0x010 34 *6edb685aSTomi Valkeinen #define CSI2_DISCARDS_LEN_LIMIT 0x014 35 *6edb685aSTomi Valkeinen 36 *6edb685aSTomi Valkeinen #define CSI2_DISCARDS_AMOUNT_SHIFT 0 37 *6edb685aSTomi Valkeinen #define CSI2_DISCARDS_AMOUNT_MASK GENMASK(23, 0) 38 *6edb685aSTomi Valkeinen #define CSI2_DISCARDS_DT_SHIFT 24 39 *6edb685aSTomi Valkeinen #define CSI2_DISCARDS_DT_MASK GENMASK(29, 24) 40 *6edb685aSTomi Valkeinen #define CSI2_DISCARDS_VC_SHIFT 30 41 *6edb685aSTomi Valkeinen #define CSI2_DISCARDS_VC_MASK GENMASK(31, 30) 42 *6edb685aSTomi Valkeinen 43 *6edb685aSTomi Valkeinen #define CSI2_LLEV_PANICS 0x018 44 *6edb685aSTomi Valkeinen #define CSI2_ULEV_PANICS 0x01c 45 *6edb685aSTomi Valkeinen #define CSI2_IRQ_MASK 0x020 46 *6edb685aSTomi Valkeinen #define CSI2_IRQ_MASK_IRQ_OVERFLOW BIT(0) 47 *6edb685aSTomi Valkeinen #define CSI2_IRQ_MASK_IRQ_DISCARD_OVERFLOW BIT(1) 48 *6edb685aSTomi Valkeinen #define CSI2_IRQ_MASK_IRQ_DISCARD_LENGTH_LIMIT BIT(2) 49 *6edb685aSTomi Valkeinen #define CSI2_IRQ_MASK_IRQ_DISCARD_UNMATCHED BIT(3) 50 *6edb685aSTomi Valkeinen #define CSI2_IRQ_MASK_IRQ_DISCARD_INACTIVE BIT(4) 51 *6edb685aSTomi Valkeinen #define CSI2_IRQ_MASK_IRQ_ALL \ 52 *6edb685aSTomi Valkeinen (CSI2_IRQ_MASK_IRQ_OVERFLOW | CSI2_IRQ_MASK_IRQ_DISCARD_OVERFLOW | \ 53 *6edb685aSTomi Valkeinen CSI2_IRQ_MASK_IRQ_DISCARD_LENGTH_LIMIT | \ 54 *6edb685aSTomi Valkeinen CSI2_IRQ_MASK_IRQ_DISCARD_UNMATCHED | \ 55 *6edb685aSTomi Valkeinen CSI2_IRQ_MASK_IRQ_DISCARD_INACTIVE) 56 *6edb685aSTomi Valkeinen 57 *6edb685aSTomi Valkeinen #define CSI2_CTRL 0x024 58 *6edb685aSTomi Valkeinen #define CSI2_CH_CTRL(x) ((x) * 0x40 + 0x28) 59 *6edb685aSTomi Valkeinen #define CSI2_CH_ADDR0(x) ((x) * 0x40 + 0x2c) 60 *6edb685aSTomi Valkeinen #define CSI2_CH_ADDR1(x) ((x) * 0x40 + 0x3c) 61 *6edb685aSTomi Valkeinen #define CSI2_CH_STRIDE(x) ((x) * 0x40 + 0x30) 62 *6edb685aSTomi Valkeinen #define CSI2_CH_LENGTH(x) ((x) * 0x40 + 0x34) 63 *6edb685aSTomi Valkeinen #define CSI2_CH_DEBUG(x) ((x) * 0x40 + 0x38) 64 *6edb685aSTomi Valkeinen #define CSI2_CH_FRAME_SIZE(x) ((x) * 0x40 + 0x40) 65 *6edb685aSTomi Valkeinen #define CSI2_CH_COMP_CTRL(x) ((x) * 0x40 + 0x44) 66 *6edb685aSTomi Valkeinen #define CSI2_CH_FE_FRAME_ID(x) ((x) * 0x40 + 0x48) 67 *6edb685aSTomi Valkeinen 68 *6edb685aSTomi Valkeinen /* CSI2_STATUS */ 69 *6edb685aSTomi Valkeinen #define CSI2_STATUS_IRQ_FS(x) (BIT(0) << (x)) 70 *6edb685aSTomi Valkeinen #define CSI2_STATUS_IRQ_FE(x) (BIT(4) << (x)) 71 *6edb685aSTomi Valkeinen #define CSI2_STATUS_IRQ_FE_ACK(x) (BIT(8) << (x)) 72 *6edb685aSTomi Valkeinen #define CSI2_STATUS_IRQ_LE(x) (BIT(12) << (x)) 73 *6edb685aSTomi Valkeinen #define CSI2_STATUS_IRQ_LE_ACK(x) (BIT(16) << (x)) 74 *6edb685aSTomi Valkeinen #define CSI2_STATUS_IRQ_CH_MASK(x) \ 75 *6edb685aSTomi Valkeinen (CSI2_STATUS_IRQ_FS(x) | CSI2_STATUS_IRQ_FE(x) | \ 76 *6edb685aSTomi Valkeinen CSI2_STATUS_IRQ_FE_ACK(x) | CSI2_STATUS_IRQ_LE(x) | \ 77 *6edb685aSTomi Valkeinen CSI2_STATUS_IRQ_LE_ACK(x)) 78 *6edb685aSTomi Valkeinen #define CSI2_STATUS_IRQ_OVERFLOW BIT(20) 79 *6edb685aSTomi Valkeinen #define CSI2_STATUS_IRQ_DISCARD_OVERFLOW BIT(21) 80 *6edb685aSTomi Valkeinen #define CSI2_STATUS_IRQ_DISCARD_LEN_LIMIT BIT(22) 81 *6edb685aSTomi Valkeinen #define CSI2_STATUS_IRQ_DISCARD_UNMATCHED BIT(23) 82 *6edb685aSTomi Valkeinen #define CSI2_STATUS_IRQ_DISCARD_INACTIVE BIT(24) 83 *6edb685aSTomi Valkeinen 84 *6edb685aSTomi Valkeinen /* CSI2_CTRL */ 85 *6edb685aSTomi Valkeinen #define CSI2_CTRL_EOP_IS_EOL BIT(0) 86 *6edb685aSTomi Valkeinen 87 *6edb685aSTomi Valkeinen /* CSI2_CH_CTRL */ 88 *6edb685aSTomi Valkeinen #define CSI2_CH_CTRL_DMA_EN BIT(0) 89 *6edb685aSTomi Valkeinen #define CSI2_CH_CTRL_FORCE BIT(3) 90 *6edb685aSTomi Valkeinen #define CSI2_CH_CTRL_AUTO_ARM BIT(4) 91 *6edb685aSTomi Valkeinen #define CSI2_CH_CTRL_IRQ_EN_FS BIT(13) 92 *6edb685aSTomi Valkeinen #define CSI2_CH_CTRL_IRQ_EN_FE BIT(14) 93 *6edb685aSTomi Valkeinen #define CSI2_CH_CTRL_IRQ_EN_FE_ACK BIT(15) 94 *6edb685aSTomi Valkeinen #define CSI2_CH_CTRL_IRQ_EN_LE BIT(16) 95 *6edb685aSTomi Valkeinen #define CSI2_CH_CTRL_IRQ_EN_LE_ACK BIT(17) 96 *6edb685aSTomi Valkeinen #define CSI2_CH_CTRL_FLUSH_FE BIT(28) 97 *6edb685aSTomi Valkeinen #define CSI2_CH_CTRL_PACK_LINE BIT(29) 98 *6edb685aSTomi Valkeinen #define CSI2_CH_CTRL_PACK_BYTES BIT(30) 99 *6edb685aSTomi Valkeinen #define CSI2_CH_CTRL_CH_MODE_MASK GENMASK(2, 1) 100 *6edb685aSTomi Valkeinen #define CSI2_CH_CTRL_VC_MASK GENMASK(6, 5) 101 *6edb685aSTomi Valkeinen #define CSI2_CH_CTRL_DT_MASK GENMASK(12, 7) 102 *6edb685aSTomi Valkeinen #define CSI2_CH_CTRL_LC_MASK GENMASK(27, 18) 103 *6edb685aSTomi Valkeinen 104 *6edb685aSTomi Valkeinen /* CHx_COMPRESSION_CONTROL */ 105 *6edb685aSTomi Valkeinen #define CSI2_CH_COMP_CTRL_OFFSET_MASK GENMASK(15, 0) 106 *6edb685aSTomi Valkeinen #define CSI2_CH_COMP_CTRL_SHIFT_MASK GENMASK(19, 16) 107 *6edb685aSTomi Valkeinen #define CSI2_CH_COMP_CTRL_MODE_MASK GENMASK(25, 24) 108 *6edb685aSTomi Valkeinen 109 *6edb685aSTomi Valkeinen static inline u32 csi2_reg_read(struct csi2_device *csi2, u32 offset) 110 *6edb685aSTomi Valkeinen { 111 *6edb685aSTomi Valkeinen return readl(csi2->base + offset); 112 *6edb685aSTomi Valkeinen } 113 *6edb685aSTomi Valkeinen 114 *6edb685aSTomi Valkeinen static inline void csi2_reg_write(struct csi2_device *csi2, u32 offset, u32 val) 115 *6edb685aSTomi Valkeinen { 116 *6edb685aSTomi Valkeinen writel(val, csi2->base + offset); 117 *6edb685aSTomi Valkeinen } 118 *6edb685aSTomi Valkeinen 119 *6edb685aSTomi Valkeinen static inline void set_field(u32 *valp, u32 field, u32 mask) 120 *6edb685aSTomi Valkeinen { 121 *6edb685aSTomi Valkeinen u32 val = *valp; 122 *6edb685aSTomi Valkeinen 123 *6edb685aSTomi Valkeinen val &= ~mask; 124 *6edb685aSTomi Valkeinen val |= (field << __ffs(mask)) & mask; 125 *6edb685aSTomi Valkeinen *valp = val; 126 *6edb685aSTomi Valkeinen } 127 *6edb685aSTomi Valkeinen 128 *6edb685aSTomi Valkeinen static int csi2_regs_show(struct seq_file *s, void *data) 129 *6edb685aSTomi Valkeinen { 130 *6edb685aSTomi Valkeinen struct csi2_device *csi2 = s->private; 131 *6edb685aSTomi Valkeinen int ret; 132 *6edb685aSTomi Valkeinen 133 *6edb685aSTomi Valkeinen ret = pm_runtime_resume_and_get(csi2->v4l2_dev->dev); 134 *6edb685aSTomi Valkeinen if (ret) 135 *6edb685aSTomi Valkeinen return ret; 136 *6edb685aSTomi Valkeinen 137 *6edb685aSTomi Valkeinen #define DUMP(reg) seq_printf(s, #reg " \t0x%08x\n", csi2_reg_read(csi2, reg)) 138 *6edb685aSTomi Valkeinen #define DUMP_CH(idx, reg) seq_printf(s, #reg "(%u) \t0x%08x\n", idx, \ 139 *6edb685aSTomi Valkeinen csi2_reg_read(csi2, reg(idx))) 140 *6edb685aSTomi Valkeinen 141 *6edb685aSTomi Valkeinen DUMP(CSI2_STATUS); 142 *6edb685aSTomi Valkeinen DUMP(CSI2_DISCARDS_OVERFLOW); 143 *6edb685aSTomi Valkeinen DUMP(CSI2_DISCARDS_INACTIVE); 144 *6edb685aSTomi Valkeinen DUMP(CSI2_DISCARDS_UNMATCHED); 145 *6edb685aSTomi Valkeinen DUMP(CSI2_DISCARDS_LEN_LIMIT); 146 *6edb685aSTomi Valkeinen DUMP(CSI2_LLEV_PANICS); 147 *6edb685aSTomi Valkeinen DUMP(CSI2_ULEV_PANICS); 148 *6edb685aSTomi Valkeinen DUMP(CSI2_IRQ_MASK); 149 *6edb685aSTomi Valkeinen DUMP(CSI2_CTRL); 150 *6edb685aSTomi Valkeinen 151 *6edb685aSTomi Valkeinen for (unsigned int i = 0; i < CSI2_NUM_CHANNELS; ++i) { 152 *6edb685aSTomi Valkeinen DUMP_CH(i, CSI2_CH_CTRL); 153 *6edb685aSTomi Valkeinen DUMP_CH(i, CSI2_CH_ADDR0); 154 *6edb685aSTomi Valkeinen DUMP_CH(i, CSI2_CH_ADDR1); 155 *6edb685aSTomi Valkeinen DUMP_CH(i, CSI2_CH_STRIDE); 156 *6edb685aSTomi Valkeinen DUMP_CH(i, CSI2_CH_LENGTH); 157 *6edb685aSTomi Valkeinen DUMP_CH(i, CSI2_CH_DEBUG); 158 *6edb685aSTomi Valkeinen DUMP_CH(i, CSI2_CH_FRAME_SIZE); 159 *6edb685aSTomi Valkeinen DUMP_CH(i, CSI2_CH_COMP_CTRL); 160 *6edb685aSTomi Valkeinen DUMP_CH(i, CSI2_CH_FE_FRAME_ID); 161 *6edb685aSTomi Valkeinen } 162 *6edb685aSTomi Valkeinen 163 *6edb685aSTomi Valkeinen #undef DUMP 164 *6edb685aSTomi Valkeinen #undef DUMP_CH 165 *6edb685aSTomi Valkeinen 166 *6edb685aSTomi Valkeinen pm_runtime_put(csi2->v4l2_dev->dev); 167 *6edb685aSTomi Valkeinen 168 *6edb685aSTomi Valkeinen return 0; 169 *6edb685aSTomi Valkeinen } 170 *6edb685aSTomi Valkeinen 171 *6edb685aSTomi Valkeinen DEFINE_SHOW_ATTRIBUTE(csi2_regs); 172 *6edb685aSTomi Valkeinen 173 *6edb685aSTomi Valkeinen static int csi2_errors_show(struct seq_file *s, void *data) 174 *6edb685aSTomi Valkeinen { 175 *6edb685aSTomi Valkeinen struct csi2_device *csi2 = s->private; 176 *6edb685aSTomi Valkeinen unsigned long flags; 177 *6edb685aSTomi Valkeinen u32 discards_table[DISCARDS_TABLE_NUM_VCS][DISCARDS_TABLE_NUM_ENTRIES]; 178 *6edb685aSTomi Valkeinen u32 discards_dt_table[DISCARDS_TABLE_NUM_ENTRIES]; 179 *6edb685aSTomi Valkeinen u32 overflows; 180 *6edb685aSTomi Valkeinen 181 *6edb685aSTomi Valkeinen spin_lock_irqsave(&csi2->errors_lock, flags); 182 *6edb685aSTomi Valkeinen 183 *6edb685aSTomi Valkeinen memcpy(discards_table, csi2->discards_table, sizeof(discards_table)); 184 *6edb685aSTomi Valkeinen memcpy(discards_dt_table, csi2->discards_dt_table, 185 *6edb685aSTomi Valkeinen sizeof(discards_dt_table)); 186 *6edb685aSTomi Valkeinen overflows = csi2->overflows; 187 *6edb685aSTomi Valkeinen 188 *6edb685aSTomi Valkeinen csi2->overflows = 0; 189 *6edb685aSTomi Valkeinen memset(csi2->discards_table, 0, sizeof(discards_table)); 190 *6edb685aSTomi Valkeinen memset(csi2->discards_dt_table, 0, sizeof(discards_dt_table)); 191 *6edb685aSTomi Valkeinen 192 *6edb685aSTomi Valkeinen spin_unlock_irqrestore(&csi2->errors_lock, flags); 193 *6edb685aSTomi Valkeinen 194 *6edb685aSTomi Valkeinen seq_printf(s, "Overflows %u\n", overflows); 195 *6edb685aSTomi Valkeinen seq_puts(s, "Discards:\n"); 196 *6edb685aSTomi Valkeinen seq_puts(s, "VC OVLF LEN UNMATCHED INACTIVE\n"); 197 *6edb685aSTomi Valkeinen 198 *6edb685aSTomi Valkeinen for (unsigned int vc = 0; vc < DISCARDS_TABLE_NUM_VCS; ++vc) { 199 *6edb685aSTomi Valkeinen seq_printf(s, "%u %10u %10u %10u %10u\n", vc, 200 *6edb685aSTomi Valkeinen discards_table[vc][DISCARDS_TABLE_OVERFLOW], 201 *6edb685aSTomi Valkeinen discards_table[vc][DISCARDS_TABLE_LENGTH_LIMIT], 202 *6edb685aSTomi Valkeinen discards_table[vc][DISCARDS_TABLE_UNMATCHED], 203 *6edb685aSTomi Valkeinen discards_table[vc][DISCARDS_TABLE_INACTIVE]); 204 *6edb685aSTomi Valkeinen } 205 *6edb685aSTomi Valkeinen 206 *6edb685aSTomi Valkeinen seq_printf(s, "Last DT %10u %10u %10u %10u\n", 207 *6edb685aSTomi Valkeinen discards_dt_table[DISCARDS_TABLE_OVERFLOW], 208 *6edb685aSTomi Valkeinen discards_dt_table[DISCARDS_TABLE_LENGTH_LIMIT], 209 *6edb685aSTomi Valkeinen discards_dt_table[DISCARDS_TABLE_UNMATCHED], 210 *6edb685aSTomi Valkeinen discards_dt_table[DISCARDS_TABLE_INACTIVE]); 211 *6edb685aSTomi Valkeinen 212 *6edb685aSTomi Valkeinen return 0; 213 *6edb685aSTomi Valkeinen } 214 *6edb685aSTomi Valkeinen 215 *6edb685aSTomi Valkeinen DEFINE_SHOW_ATTRIBUTE(csi2_errors); 216 *6edb685aSTomi Valkeinen 217 *6edb685aSTomi Valkeinen static void csi2_isr_handle_errors(struct csi2_device *csi2, u32 status) 218 *6edb685aSTomi Valkeinen { 219 *6edb685aSTomi Valkeinen spin_lock(&csi2->errors_lock); 220 *6edb685aSTomi Valkeinen 221 *6edb685aSTomi Valkeinen if (status & CSI2_STATUS_IRQ_OVERFLOW) 222 *6edb685aSTomi Valkeinen csi2->overflows++; 223 *6edb685aSTomi Valkeinen 224 *6edb685aSTomi Valkeinen for (unsigned int i = 0; i < DISCARDS_TABLE_NUM_ENTRIES; ++i) { 225 *6edb685aSTomi Valkeinen static const u32 discard_bits[] = { 226 *6edb685aSTomi Valkeinen CSI2_STATUS_IRQ_DISCARD_OVERFLOW, 227 *6edb685aSTomi Valkeinen CSI2_STATUS_IRQ_DISCARD_LEN_LIMIT, 228 *6edb685aSTomi Valkeinen CSI2_STATUS_IRQ_DISCARD_UNMATCHED, 229 *6edb685aSTomi Valkeinen CSI2_STATUS_IRQ_DISCARD_INACTIVE, 230 *6edb685aSTomi Valkeinen }; 231 *6edb685aSTomi Valkeinen static const u8 discard_regs[] = { 232 *6edb685aSTomi Valkeinen CSI2_DISCARDS_OVERFLOW, 233 *6edb685aSTomi Valkeinen CSI2_DISCARDS_LEN_LIMIT, 234 *6edb685aSTomi Valkeinen CSI2_DISCARDS_UNMATCHED, 235 *6edb685aSTomi Valkeinen CSI2_DISCARDS_INACTIVE, 236 *6edb685aSTomi Valkeinen }; 237 *6edb685aSTomi Valkeinen u32 amount; 238 *6edb685aSTomi Valkeinen u8 dt, vc; 239 *6edb685aSTomi Valkeinen u32 v; 240 *6edb685aSTomi Valkeinen 241 *6edb685aSTomi Valkeinen if (!(status & discard_bits[i])) 242 *6edb685aSTomi Valkeinen continue; 243 *6edb685aSTomi Valkeinen 244 *6edb685aSTomi Valkeinen v = csi2_reg_read(csi2, discard_regs[i]); 245 *6edb685aSTomi Valkeinen csi2_reg_write(csi2, discard_regs[i], 0); 246 *6edb685aSTomi Valkeinen 247 *6edb685aSTomi Valkeinen amount = (v & CSI2_DISCARDS_AMOUNT_MASK) >> 248 *6edb685aSTomi Valkeinen CSI2_DISCARDS_AMOUNT_SHIFT; 249 *6edb685aSTomi Valkeinen dt = (v & CSI2_DISCARDS_DT_MASK) >> CSI2_DISCARDS_DT_SHIFT; 250 *6edb685aSTomi Valkeinen vc = (v & CSI2_DISCARDS_VC_MASK) >> CSI2_DISCARDS_VC_SHIFT; 251 *6edb685aSTomi Valkeinen 252 *6edb685aSTomi Valkeinen csi2->discards_table[vc][i] += amount; 253 *6edb685aSTomi Valkeinen csi2->discards_dt_table[i] = dt; 254 *6edb685aSTomi Valkeinen } 255 *6edb685aSTomi Valkeinen 256 *6edb685aSTomi Valkeinen spin_unlock(&csi2->errors_lock); 257 *6edb685aSTomi Valkeinen } 258 *6edb685aSTomi Valkeinen 259 *6edb685aSTomi Valkeinen void csi2_isr(struct csi2_device *csi2, bool *sof, bool *eof) 260 *6edb685aSTomi Valkeinen { 261 *6edb685aSTomi Valkeinen u32 status; 262 *6edb685aSTomi Valkeinen 263 *6edb685aSTomi Valkeinen status = csi2_reg_read(csi2, CSI2_STATUS); 264 *6edb685aSTomi Valkeinen 265 *6edb685aSTomi Valkeinen /* Write value back to clear the interrupts */ 266 *6edb685aSTomi Valkeinen csi2_reg_write(csi2, CSI2_STATUS, status); 267 *6edb685aSTomi Valkeinen 268 *6edb685aSTomi Valkeinen for (unsigned int i = 0; i < CSI2_NUM_CHANNELS; i++) { 269 *6edb685aSTomi Valkeinen u32 dbg; 270 *6edb685aSTomi Valkeinen 271 *6edb685aSTomi Valkeinen if ((status & CSI2_STATUS_IRQ_CH_MASK(i)) == 0) 272 *6edb685aSTomi Valkeinen continue; 273 *6edb685aSTomi Valkeinen 274 *6edb685aSTomi Valkeinen dbg = csi2_reg_read(csi2, CSI2_CH_DEBUG(i)); 275 *6edb685aSTomi Valkeinen 276 *6edb685aSTomi Valkeinen trace_csi2_irq(i, status, dbg); 277 *6edb685aSTomi Valkeinen 278 *6edb685aSTomi Valkeinen sof[i] = !!(status & CSI2_STATUS_IRQ_FS(i)); 279 *6edb685aSTomi Valkeinen eof[i] = !!(status & CSI2_STATUS_IRQ_FE_ACK(i)); 280 *6edb685aSTomi Valkeinen } 281 *6edb685aSTomi Valkeinen 282 *6edb685aSTomi Valkeinen if (csi2_track_errors) 283 *6edb685aSTomi Valkeinen csi2_isr_handle_errors(csi2, status); 284 *6edb685aSTomi Valkeinen } 285 *6edb685aSTomi Valkeinen 286 *6edb685aSTomi Valkeinen void csi2_set_buffer(struct csi2_device *csi2, unsigned int channel, 287 *6edb685aSTomi Valkeinen dma_addr_t dmaaddr, unsigned int stride, unsigned int size) 288 *6edb685aSTomi Valkeinen { 289 *6edb685aSTomi Valkeinen u64 addr = dmaaddr; 290 *6edb685aSTomi Valkeinen /* 291 *6edb685aSTomi Valkeinen * ADDRESS0 must be written last as it triggers the double buffering 292 *6edb685aSTomi Valkeinen * mechanism for all buffer registers within the hardware. 293 *6edb685aSTomi Valkeinen */ 294 *6edb685aSTomi Valkeinen addr >>= 4; 295 *6edb685aSTomi Valkeinen csi2_reg_write(csi2, CSI2_CH_LENGTH(channel), size >> 4); 296 *6edb685aSTomi Valkeinen csi2_reg_write(csi2, CSI2_CH_STRIDE(channel), stride >> 4); 297 *6edb685aSTomi Valkeinen csi2_reg_write(csi2, CSI2_CH_ADDR1(channel), addr >> 32); 298 *6edb685aSTomi Valkeinen csi2_reg_write(csi2, CSI2_CH_ADDR0(channel), addr & 0xffffffff); 299 *6edb685aSTomi Valkeinen } 300 *6edb685aSTomi Valkeinen 301 *6edb685aSTomi Valkeinen void csi2_set_compression(struct csi2_device *csi2, unsigned int channel, 302 *6edb685aSTomi Valkeinen enum csi2_compression_mode mode, unsigned int shift, 303 *6edb685aSTomi Valkeinen unsigned int offset) 304 *6edb685aSTomi Valkeinen { 305 *6edb685aSTomi Valkeinen u32 compression = 0; 306 *6edb685aSTomi Valkeinen 307 *6edb685aSTomi Valkeinen set_field(&compression, CSI2_CH_COMP_CTRL_OFFSET_MASK, offset); 308 *6edb685aSTomi Valkeinen set_field(&compression, CSI2_CH_COMP_CTRL_SHIFT_MASK, shift); 309 *6edb685aSTomi Valkeinen set_field(&compression, CSI2_CH_COMP_CTRL_MODE_MASK, mode); 310 *6edb685aSTomi Valkeinen csi2_reg_write(csi2, CSI2_CH_COMP_CTRL(channel), compression); 311 *6edb685aSTomi Valkeinen } 312 *6edb685aSTomi Valkeinen 313 *6edb685aSTomi Valkeinen void csi2_start_channel(struct csi2_device *csi2, unsigned int channel, 314 *6edb685aSTomi Valkeinen enum csi2_mode mode, bool auto_arm, bool pack_bytes, 315 *6edb685aSTomi Valkeinen unsigned int width, unsigned int height, 316 *6edb685aSTomi Valkeinen u8 vc, u8 dt) 317 *6edb685aSTomi Valkeinen { 318 *6edb685aSTomi Valkeinen u32 ctrl; 319 *6edb685aSTomi Valkeinen 320 *6edb685aSTomi Valkeinen csi2_dbg(csi2, "%s [%u]\n", __func__, channel); 321 *6edb685aSTomi Valkeinen 322 *6edb685aSTomi Valkeinen csi2_reg_write(csi2, CSI2_CH_CTRL(channel), 0); 323 *6edb685aSTomi Valkeinen csi2_reg_write(csi2, CSI2_CH_DEBUG(channel), 0); 324 *6edb685aSTomi Valkeinen csi2_reg_write(csi2, CSI2_STATUS, CSI2_STATUS_IRQ_CH_MASK(channel)); 325 *6edb685aSTomi Valkeinen 326 *6edb685aSTomi Valkeinen /* Enable channel and FS/FE interrupts. */ 327 *6edb685aSTomi Valkeinen ctrl = CSI2_CH_CTRL_DMA_EN | CSI2_CH_CTRL_IRQ_EN_FS | 328 *6edb685aSTomi Valkeinen CSI2_CH_CTRL_IRQ_EN_FE_ACK | CSI2_CH_CTRL_PACK_LINE; 329 *6edb685aSTomi Valkeinen /* PACK_BYTES ensures no striding for embedded data. */ 330 *6edb685aSTomi Valkeinen if (pack_bytes) 331 *6edb685aSTomi Valkeinen ctrl |= CSI2_CH_CTRL_PACK_BYTES; 332 *6edb685aSTomi Valkeinen 333 *6edb685aSTomi Valkeinen if (auto_arm) 334 *6edb685aSTomi Valkeinen ctrl |= CSI2_CH_CTRL_AUTO_ARM; 335 *6edb685aSTomi Valkeinen 336 *6edb685aSTomi Valkeinen if (width && height) { 337 *6edb685aSTomi Valkeinen set_field(&ctrl, mode, CSI2_CH_CTRL_CH_MODE_MASK); 338 *6edb685aSTomi Valkeinen csi2_reg_write(csi2, CSI2_CH_FRAME_SIZE(channel), 339 *6edb685aSTomi Valkeinen (height << 16) | width); 340 *6edb685aSTomi Valkeinen } else { 341 *6edb685aSTomi Valkeinen set_field(&ctrl, 0x0, CSI2_CH_CTRL_CH_MODE_MASK); 342 *6edb685aSTomi Valkeinen csi2_reg_write(csi2, CSI2_CH_FRAME_SIZE(channel), 0); 343 *6edb685aSTomi Valkeinen } 344 *6edb685aSTomi Valkeinen 345 *6edb685aSTomi Valkeinen set_field(&ctrl, vc, CSI2_CH_CTRL_VC_MASK); 346 *6edb685aSTomi Valkeinen set_field(&ctrl, dt, CSI2_CH_CTRL_DT_MASK); 347 *6edb685aSTomi Valkeinen csi2_reg_write(csi2, CSI2_CH_CTRL(channel), ctrl); 348 *6edb685aSTomi Valkeinen csi2->num_lines[channel] = height; 349 *6edb685aSTomi Valkeinen } 350 *6edb685aSTomi Valkeinen 351 *6edb685aSTomi Valkeinen void csi2_stop_channel(struct csi2_device *csi2, unsigned int channel) 352 *6edb685aSTomi Valkeinen { 353 *6edb685aSTomi Valkeinen csi2_dbg(csi2, "%s [%u]\n", __func__, channel); 354 *6edb685aSTomi Valkeinen 355 *6edb685aSTomi Valkeinen /* Channel disable. Use FORCE to allow stopping mid-frame. */ 356 *6edb685aSTomi Valkeinen csi2_reg_write(csi2, CSI2_CH_CTRL(channel), CSI2_CH_CTRL_FORCE); 357 *6edb685aSTomi Valkeinen /* Latch the above change by writing to the ADDR0 register. */ 358 *6edb685aSTomi Valkeinen csi2_reg_write(csi2, CSI2_CH_ADDR0(channel), 0); 359 *6edb685aSTomi Valkeinen /* Write this again, the HW needs it! */ 360 *6edb685aSTomi Valkeinen csi2_reg_write(csi2, CSI2_CH_ADDR0(channel), 0); 361 *6edb685aSTomi Valkeinen } 362 *6edb685aSTomi Valkeinen 363 *6edb685aSTomi Valkeinen void csi2_open_rx(struct csi2_device *csi2) 364 *6edb685aSTomi Valkeinen { 365 *6edb685aSTomi Valkeinen csi2_reg_write(csi2, CSI2_IRQ_MASK, 366 *6edb685aSTomi Valkeinen csi2_track_errors ? CSI2_IRQ_MASK_IRQ_ALL : 0); 367 *6edb685aSTomi Valkeinen 368 *6edb685aSTomi Valkeinen dphy_start(&csi2->dphy); 369 *6edb685aSTomi Valkeinen 370 *6edb685aSTomi Valkeinen csi2_reg_write(csi2, CSI2_CTRL, CSI2_CTRL_EOP_IS_EOL); 371 *6edb685aSTomi Valkeinen } 372 *6edb685aSTomi Valkeinen 373 *6edb685aSTomi Valkeinen void csi2_close_rx(struct csi2_device *csi2) 374 *6edb685aSTomi Valkeinen { 375 *6edb685aSTomi Valkeinen dphy_stop(&csi2->dphy); 376 *6edb685aSTomi Valkeinen 377 *6edb685aSTomi Valkeinen csi2_reg_write(csi2, CSI2_IRQ_MASK, 0); 378 *6edb685aSTomi Valkeinen } 379 *6edb685aSTomi Valkeinen 380 *6edb685aSTomi Valkeinen static int csi2_init_state(struct v4l2_subdev *sd, 381 *6edb685aSTomi Valkeinen struct v4l2_subdev_state *state) 382 *6edb685aSTomi Valkeinen { 383 *6edb685aSTomi Valkeinen struct v4l2_subdev_route routes[] = { { 384 *6edb685aSTomi Valkeinen .sink_pad = CSI2_PAD_SINK, 385 *6edb685aSTomi Valkeinen .sink_stream = 0, 386 *6edb685aSTomi Valkeinen .source_pad = CSI2_PAD_FIRST_SOURCE, 387 *6edb685aSTomi Valkeinen .source_stream = 0, 388 *6edb685aSTomi Valkeinen .flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE, 389 *6edb685aSTomi Valkeinen } }; 390 *6edb685aSTomi Valkeinen 391 *6edb685aSTomi Valkeinen struct v4l2_subdev_krouting routing = { 392 *6edb685aSTomi Valkeinen .num_routes = ARRAY_SIZE(routes), 393 *6edb685aSTomi Valkeinen .routes = routes, 394 *6edb685aSTomi Valkeinen }; 395 *6edb685aSTomi Valkeinen 396 *6edb685aSTomi Valkeinen int ret; 397 *6edb685aSTomi Valkeinen 398 *6edb685aSTomi Valkeinen ret = v4l2_subdev_set_routing_with_fmt(sd, state, &routing, 399 *6edb685aSTomi Valkeinen &cfe_default_format); 400 *6edb685aSTomi Valkeinen if (ret) 401 *6edb685aSTomi Valkeinen return ret; 402 *6edb685aSTomi Valkeinen 403 *6edb685aSTomi Valkeinen return 0; 404 *6edb685aSTomi Valkeinen } 405 *6edb685aSTomi Valkeinen 406 *6edb685aSTomi Valkeinen static int csi2_pad_set_fmt(struct v4l2_subdev *sd, 407 *6edb685aSTomi Valkeinen struct v4l2_subdev_state *state, 408 *6edb685aSTomi Valkeinen struct v4l2_subdev_format *format) 409 *6edb685aSTomi Valkeinen { 410 *6edb685aSTomi Valkeinen if (format->pad == CSI2_PAD_SINK) { 411 *6edb685aSTomi Valkeinen /* Store the sink format and propagate it to the source. */ 412 *6edb685aSTomi Valkeinen 413 *6edb685aSTomi Valkeinen const struct cfe_fmt *cfe_fmt; 414 *6edb685aSTomi Valkeinen 415 *6edb685aSTomi Valkeinen cfe_fmt = find_format_by_code(format->format.code); 416 *6edb685aSTomi Valkeinen if (!cfe_fmt) { 417 *6edb685aSTomi Valkeinen cfe_fmt = find_format_by_code(MEDIA_BUS_FMT_SRGGB10_1X10); 418 *6edb685aSTomi Valkeinen format->format.code = cfe_fmt->code; 419 *6edb685aSTomi Valkeinen } 420 *6edb685aSTomi Valkeinen 421 *6edb685aSTomi Valkeinen struct v4l2_mbus_framefmt *fmt; 422 *6edb685aSTomi Valkeinen 423 *6edb685aSTomi Valkeinen fmt = v4l2_subdev_state_get_format(state, format->pad, 424 *6edb685aSTomi Valkeinen format->stream); 425 *6edb685aSTomi Valkeinen if (!fmt) 426 *6edb685aSTomi Valkeinen return -EINVAL; 427 *6edb685aSTomi Valkeinen 428 *6edb685aSTomi Valkeinen *fmt = format->format; 429 *6edb685aSTomi Valkeinen 430 *6edb685aSTomi Valkeinen fmt = v4l2_subdev_state_get_opposite_stream_format(state, 431 *6edb685aSTomi Valkeinen format->pad, 432 *6edb685aSTomi Valkeinen format->stream); 433 *6edb685aSTomi Valkeinen if (!fmt) 434 *6edb685aSTomi Valkeinen return -EINVAL; 435 *6edb685aSTomi Valkeinen 436 *6edb685aSTomi Valkeinen format->format.field = V4L2_FIELD_NONE; 437 *6edb685aSTomi Valkeinen 438 *6edb685aSTomi Valkeinen *fmt = format->format; 439 *6edb685aSTomi Valkeinen } else { 440 *6edb685aSTomi Valkeinen /* Only allow changing the source pad mbus code. */ 441 *6edb685aSTomi Valkeinen 442 *6edb685aSTomi Valkeinen struct v4l2_mbus_framefmt *sink_fmt, *source_fmt; 443 *6edb685aSTomi Valkeinen u32 sink_code; 444 *6edb685aSTomi Valkeinen u32 code; 445 *6edb685aSTomi Valkeinen 446 *6edb685aSTomi Valkeinen sink_fmt = v4l2_subdev_state_get_opposite_stream_format(state, 447 *6edb685aSTomi Valkeinen format->pad, 448 *6edb685aSTomi Valkeinen format->stream); 449 *6edb685aSTomi Valkeinen if (!sink_fmt) 450 *6edb685aSTomi Valkeinen return -EINVAL; 451 *6edb685aSTomi Valkeinen 452 *6edb685aSTomi Valkeinen source_fmt = v4l2_subdev_state_get_format(state, format->pad, 453 *6edb685aSTomi Valkeinen format->stream); 454 *6edb685aSTomi Valkeinen if (!source_fmt) 455 *6edb685aSTomi Valkeinen return -EINVAL; 456 *6edb685aSTomi Valkeinen 457 *6edb685aSTomi Valkeinen sink_code = sink_fmt->code; 458 *6edb685aSTomi Valkeinen code = format->format.code; 459 *6edb685aSTomi Valkeinen 460 *6edb685aSTomi Valkeinen /* 461 *6edb685aSTomi Valkeinen * Only allow changing the mbus code to: 462 *6edb685aSTomi Valkeinen * - The sink's mbus code 463 *6edb685aSTomi Valkeinen * - The 16-bit version of the sink's mbus code 464 *6edb685aSTomi Valkeinen * - The compressed version of the sink's mbus code 465 *6edb685aSTomi Valkeinen */ 466 *6edb685aSTomi Valkeinen if (code == sink_code || 467 *6edb685aSTomi Valkeinen code == cfe_find_16bit_code(sink_code) || 468 *6edb685aSTomi Valkeinen code == cfe_find_compressed_code(sink_code)) 469 *6edb685aSTomi Valkeinen source_fmt->code = code; 470 *6edb685aSTomi Valkeinen 471 *6edb685aSTomi Valkeinen format->format.code = source_fmt->code; 472 *6edb685aSTomi Valkeinen } 473 *6edb685aSTomi Valkeinen 474 *6edb685aSTomi Valkeinen return 0; 475 *6edb685aSTomi Valkeinen } 476 *6edb685aSTomi Valkeinen 477 *6edb685aSTomi Valkeinen static int csi2_set_routing(struct v4l2_subdev *sd, 478 *6edb685aSTomi Valkeinen struct v4l2_subdev_state *state, 479 *6edb685aSTomi Valkeinen enum v4l2_subdev_format_whence which, 480 *6edb685aSTomi Valkeinen struct v4l2_subdev_krouting *routing) 481 *6edb685aSTomi Valkeinen { 482 *6edb685aSTomi Valkeinen int ret; 483 *6edb685aSTomi Valkeinen 484 *6edb685aSTomi Valkeinen ret = v4l2_subdev_routing_validate(sd, routing, 485 *6edb685aSTomi Valkeinen V4L2_SUBDEV_ROUTING_ONLY_1_TO_1 | 486 *6edb685aSTomi Valkeinen V4L2_SUBDEV_ROUTING_NO_SOURCE_MULTIPLEXING); 487 *6edb685aSTomi Valkeinen if (ret) 488 *6edb685aSTomi Valkeinen return ret; 489 *6edb685aSTomi Valkeinen 490 *6edb685aSTomi Valkeinen /* Only stream ID 0 allowed on source pads */ 491 *6edb685aSTomi Valkeinen for (unsigned int i = 0; i < routing->num_routes; ++i) { 492 *6edb685aSTomi Valkeinen const struct v4l2_subdev_route *route = &routing->routes[i]; 493 *6edb685aSTomi Valkeinen 494 *6edb685aSTomi Valkeinen if (route->source_stream != 0) 495 *6edb685aSTomi Valkeinen return -EINVAL; 496 *6edb685aSTomi Valkeinen } 497 *6edb685aSTomi Valkeinen 498 *6edb685aSTomi Valkeinen ret = v4l2_subdev_set_routing_with_fmt(sd, state, routing, 499 *6edb685aSTomi Valkeinen &cfe_default_format); 500 *6edb685aSTomi Valkeinen if (ret) 501 *6edb685aSTomi Valkeinen return ret; 502 *6edb685aSTomi Valkeinen 503 *6edb685aSTomi Valkeinen return 0; 504 *6edb685aSTomi Valkeinen } 505 *6edb685aSTomi Valkeinen 506 *6edb685aSTomi Valkeinen static const struct v4l2_subdev_pad_ops csi2_subdev_pad_ops = { 507 *6edb685aSTomi Valkeinen .get_fmt = v4l2_subdev_get_fmt, 508 *6edb685aSTomi Valkeinen .set_fmt = csi2_pad_set_fmt, 509 *6edb685aSTomi Valkeinen .set_routing = csi2_set_routing, 510 *6edb685aSTomi Valkeinen .link_validate = v4l2_subdev_link_validate_default, 511 *6edb685aSTomi Valkeinen }; 512 *6edb685aSTomi Valkeinen 513 *6edb685aSTomi Valkeinen static const struct media_entity_operations csi2_entity_ops = { 514 *6edb685aSTomi Valkeinen .link_validate = v4l2_subdev_link_validate, 515 *6edb685aSTomi Valkeinen .has_pad_interdep = v4l2_subdev_has_pad_interdep, 516 *6edb685aSTomi Valkeinen }; 517 *6edb685aSTomi Valkeinen 518 *6edb685aSTomi Valkeinen static const struct v4l2_subdev_ops csi2_subdev_ops = { 519 *6edb685aSTomi Valkeinen .pad = &csi2_subdev_pad_ops, 520 *6edb685aSTomi Valkeinen }; 521 *6edb685aSTomi Valkeinen 522 *6edb685aSTomi Valkeinen static const struct v4l2_subdev_internal_ops csi2_internal_ops = { 523 *6edb685aSTomi Valkeinen .init_state = csi2_init_state, 524 *6edb685aSTomi Valkeinen }; 525 *6edb685aSTomi Valkeinen 526 *6edb685aSTomi Valkeinen int csi2_init(struct csi2_device *csi2, struct dentry *debugfs) 527 *6edb685aSTomi Valkeinen { 528 *6edb685aSTomi Valkeinen unsigned int ret; 529 *6edb685aSTomi Valkeinen 530 *6edb685aSTomi Valkeinen spin_lock_init(&csi2->errors_lock); 531 *6edb685aSTomi Valkeinen 532 *6edb685aSTomi Valkeinen csi2->dphy.dev = csi2->v4l2_dev->dev; 533 *6edb685aSTomi Valkeinen dphy_probe(&csi2->dphy); 534 *6edb685aSTomi Valkeinen 535 *6edb685aSTomi Valkeinen debugfs_create_file("csi2_regs", 0440, debugfs, csi2, &csi2_regs_fops); 536 *6edb685aSTomi Valkeinen 537 *6edb685aSTomi Valkeinen if (csi2_track_errors) 538 *6edb685aSTomi Valkeinen debugfs_create_file("csi2_errors", 0440, debugfs, csi2, 539 *6edb685aSTomi Valkeinen &csi2_errors_fops); 540 *6edb685aSTomi Valkeinen 541 *6edb685aSTomi Valkeinen csi2->pad[CSI2_PAD_SINK].flags = MEDIA_PAD_FL_SINK; 542 *6edb685aSTomi Valkeinen 543 *6edb685aSTomi Valkeinen for (unsigned int i = CSI2_PAD_FIRST_SOURCE; 544 *6edb685aSTomi Valkeinen i < CSI2_PAD_FIRST_SOURCE + CSI2_PAD_NUM_SOURCES; i++) 545 *6edb685aSTomi Valkeinen csi2->pad[i].flags = MEDIA_PAD_FL_SOURCE; 546 *6edb685aSTomi Valkeinen 547 *6edb685aSTomi Valkeinen ret = media_entity_pads_init(&csi2->sd.entity, ARRAY_SIZE(csi2->pad), 548 *6edb685aSTomi Valkeinen csi2->pad); 549 *6edb685aSTomi Valkeinen if (ret) 550 *6edb685aSTomi Valkeinen return ret; 551 *6edb685aSTomi Valkeinen 552 *6edb685aSTomi Valkeinen /* Initialize subdev */ 553 *6edb685aSTomi Valkeinen v4l2_subdev_init(&csi2->sd, &csi2_subdev_ops); 554 *6edb685aSTomi Valkeinen csi2->sd.internal_ops = &csi2_internal_ops; 555 *6edb685aSTomi Valkeinen csi2->sd.entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; 556 *6edb685aSTomi Valkeinen csi2->sd.entity.ops = &csi2_entity_ops; 557 *6edb685aSTomi Valkeinen csi2->sd.flags = V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_STREAMS; 558 *6edb685aSTomi Valkeinen csi2->sd.owner = THIS_MODULE; 559 *6edb685aSTomi Valkeinen snprintf(csi2->sd.name, sizeof(csi2->sd.name), "csi2"); 560 *6edb685aSTomi Valkeinen 561 *6edb685aSTomi Valkeinen ret = v4l2_subdev_init_finalize(&csi2->sd); 562 *6edb685aSTomi Valkeinen if (ret) 563 *6edb685aSTomi Valkeinen goto err_entity_cleanup; 564 *6edb685aSTomi Valkeinen 565 *6edb685aSTomi Valkeinen ret = v4l2_device_register_subdev(csi2->v4l2_dev, &csi2->sd); 566 *6edb685aSTomi Valkeinen if (ret) { 567 *6edb685aSTomi Valkeinen csi2_err(csi2, "Failed register csi2 subdev (%d)\n", ret); 568 *6edb685aSTomi Valkeinen goto err_subdev_cleanup; 569 *6edb685aSTomi Valkeinen } 570 *6edb685aSTomi Valkeinen 571 *6edb685aSTomi Valkeinen return 0; 572 *6edb685aSTomi Valkeinen 573 *6edb685aSTomi Valkeinen err_subdev_cleanup: 574 *6edb685aSTomi Valkeinen v4l2_subdev_cleanup(&csi2->sd); 575 *6edb685aSTomi Valkeinen err_entity_cleanup: 576 *6edb685aSTomi Valkeinen media_entity_cleanup(&csi2->sd.entity); 577 *6edb685aSTomi Valkeinen 578 *6edb685aSTomi Valkeinen return ret; 579 *6edb685aSTomi Valkeinen } 580 *6edb685aSTomi Valkeinen 581 *6edb685aSTomi Valkeinen void csi2_uninit(struct csi2_device *csi2) 582 *6edb685aSTomi Valkeinen { 583 *6edb685aSTomi Valkeinen v4l2_device_unregister_subdev(&csi2->sd); 584 *6edb685aSTomi Valkeinen v4l2_subdev_cleanup(&csi2->sd); 585 *6edb685aSTomi Valkeinen media_entity_cleanup(&csi2->sd.entity); 586 *6edb685aSTomi Valkeinen } 587