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