/* * Configure the bridge. Valid inputs are * * IPIPEIF_INPUT_CSI2A: CSI2a receiver * IPIPEIF_INPUT_CSI2B: CSI2b receiver * * The bridge and lane shifter are configured according to the selected input * and the ISP platform data. */ void omap4iss_configure_bridge(struct iss_device *iss, enum ipipeif_input_entity input) { u32 issctrl_val; u32 isp5ctrl_val; issctrl_val = iss_reg_read(iss, OMAP4_ISS_MEM_TOP, ISS_CTRL); issctrl_val &= ~ISS_CTRL_INPUT_SEL_MASK; issctrl_val &= ~ISS_CTRL_CLK_DIV_MASK; isp5ctrl_val = iss_reg_read(iss, OMAP4_ISS_MEM_ISP_SYS1, ISP5_CTRL); switch (input) { case IPIPEIF_INPUT_CSI2A: issctrl_val |= ISS_CTRL_INPUT_SEL_CSI2A; break; case IPIPEIF_INPUT_CSI2B: issctrl_val |= ISS_CTRL_INPUT_SEL_CSI2B; break; default: return; } issctrl_val |= ISS_CTRL_SYNC_DETECT_VS_RAISING; isp5ctrl_val |= ISP5_CTRL_VD_PULSE_EXT | ISP5_CTRL_PSYNC_CLK_SEL | ISP5_CTRL_SYNC_ENABLE; iss_reg_write(iss, OMAP4_ISS_MEM_TOP, ISS_CTRL, issctrl_val); iss_reg_write(iss, OMAP4_ISS_MEM_ISP_SYS1, ISP5_CTRL, isp5ctrl_val); }
static int iss_isp_reset(struct iss_device *iss) { unsigned int timeout; /* Fist, ensure that the ISP is IDLE (no transactions happening) */ iss_reg_update(iss, OMAP4_ISS_MEM_ISP_SYS1, ISP5_SYSCONFIG, ISP5_SYSCONFIG_STANDBYMODE_MASK, ISP5_SYSCONFIG_STANDBYMODE_SMART); iss_reg_set(iss, OMAP4_ISS_MEM_ISP_SYS1, ISP5_CTRL, ISP5_CTRL_MSTANDBY); timeout = iss_poll_condition_timeout( iss_reg_read(iss, OMAP4_ISS_MEM_ISP_SYS1, ISP5_CTRL) & ISP5_CTRL_MSTANDBY_WAIT, 1000000, 1000, 1500); if (timeout) { dev_err(iss->dev, "ISP5 standby timeout\n"); return -ETIMEDOUT; } /* Now finally, do the reset */ iss_reg_set(iss, OMAP4_ISS_MEM_ISP_SYS1, ISP5_SYSCONFIG, ISP5_SYSCONFIG_SOFTRESET); timeout = iss_poll_condition_timeout( !(iss_reg_read(iss, OMAP4_ISS_MEM_ISP_SYS1, ISP5_SYSCONFIG) & ISP5_SYSCONFIG_SOFTRESET), 1000000, 1000, 1500); if (timeout) { dev_err(iss->dev, "ISP5 reset timeout\n"); return -ETIMEDOUT; } return 0; }
/* * iss_isr - Interrupt Service Routine for ISS module. * @irq: Not used currently. * @_iss: Pointer to the OMAP4 ISS device * * Handles the corresponding callback if plugged in. * * Returns IRQ_HANDLED when IRQ was correctly handled, or IRQ_NONE when the * IRQ wasn't handled. */ static irqreturn_t iss_isr(int irq, void *_iss) { static const u32 ipipeif_events = ISP5_IRQ_IPIPEIF_IRQ | ISP5_IRQ_ISIF_INT(0); static const u32 resizer_events = ISP5_IRQ_RSZ_FIFO_IN_BLK_ERR | ISP5_IRQ_RSZ_FIFO_OVF | ISP5_IRQ_RSZ_INT_DMA; struct iss_device *iss = _iss; u32 irqstatus; irqstatus = iss_reg_read(iss, OMAP4_ISS_MEM_TOP, ISS_HL_IRQSTATUS(5)); iss_reg_write(iss, OMAP4_ISS_MEM_TOP, ISS_HL_IRQSTATUS(5), irqstatus); if (irqstatus & ISS_HL_IRQ_CSIA) omap4iss_csi2_isr(&iss->csi2a); if (irqstatus & ISS_HL_IRQ_CSIB) omap4iss_csi2_isr(&iss->csi2b); if (irqstatus & ISS_HL_IRQ_ISP(0)) { u32 isp_irqstatus = iss_reg_read(iss, OMAP4_ISS_MEM_ISP_SYS1, ISP5_IRQSTATUS(0)); iss_reg_write(iss, OMAP4_ISS_MEM_ISP_SYS1, ISP5_IRQSTATUS(0), isp_irqstatus); if (isp_irqstatus & ISP5_IRQ_OCP_ERR) dev_dbg(iss->dev, "ISP5 OCP Error!\n"); if (isp_irqstatus & ipipeif_events) { omap4iss_ipipeif_isr(&iss->ipipeif, isp_irqstatus & ipipeif_events); } if (isp_irqstatus & resizer_events) omap4iss_resizer_isr(&iss->resizer, isp_irqstatus & resizer_events); #ifdef ISS_ISR_DEBUG iss_isp_isr_dbg(iss, isp_irqstatus); #endif } omap4iss_flush(iss); #ifdef ISS_ISR_DEBUG iss_isr_dbg(iss, irqstatus); #endif return IRQ_HANDLED; }
/* * omap4iss_csi2_isr - CSI2 interrupt handling. */ void omap4iss_csi2_isr(struct iss_csi2_device *csi2) { struct iss_pipeline *pipe = to_iss_pipeline(&csi2->subdev.entity); u32 csi2_irqstatus, cpxio1_irqstatus; struct iss_device *iss = csi2->iss; if (!csi2->available) return; csi2_irqstatus = iss_reg_read(csi2->iss, csi2->regs1, CSI2_IRQSTATUS); iss_reg_write(csi2->iss, csi2->regs1, CSI2_IRQSTATUS, csi2_irqstatus); /* Failure Cases */ if (csi2_irqstatus & CSI2_IRQ_COMPLEXIO_ERR) { cpxio1_irqstatus = iss_reg_read(csi2->iss, csi2->regs1, CSI2_COMPLEXIO_IRQSTATUS); iss_reg_write(csi2->iss, csi2->regs1, CSI2_COMPLEXIO_IRQSTATUS, cpxio1_irqstatus); dev_dbg(iss->dev, "CSI2: ComplexIO Error IRQ %x\n", cpxio1_irqstatus); pipe->error = true; } if (csi2_irqstatus & (CSI2_IRQ_OCP_ERR | CSI2_IRQ_SHORT_PACKET | CSI2_IRQ_ECC_NO_CORRECTION | CSI2_IRQ_COMPLEXIO_ERR | CSI2_IRQ_FIFO_OVF)) { dev_dbg(iss->dev, "CSI2 Err: OCP:%d SHORT:%d ECC:%d CPXIO:%d OVF:%d\n", csi2_irqstatus & CSI2_IRQ_OCP_ERR ? 1 : 0, csi2_irqstatus & CSI2_IRQ_SHORT_PACKET ? 1 : 0, csi2_irqstatus & CSI2_IRQ_ECC_NO_CORRECTION ? 1 : 0, csi2_irqstatus & CSI2_IRQ_COMPLEXIO_ERR ? 1 : 0, csi2_irqstatus & CSI2_IRQ_FIFO_OVF ? 1 : 0); pipe->error = true; } if (omap4iss_module_sync_is_stopping(&csi2->wait, &csi2->stopping)) return; /* Successful cases */ if (csi2_irqstatus & CSI2_IRQ_CONTEXT0) csi2_isr_ctx(csi2, &csi2->contexts[0]); if (csi2_irqstatus & CSI2_IRQ_ECC_CORRECTION) dev_dbg(iss->dev, "CSI2: ECC correction done\n"); }
/* * csi2_timing_config - CSI2 timing configuration. * @timing: csi2_timing_cfg structure */ static void csi2_timing_config(struct iss_csi2_device *csi2, struct iss_csi2_timing_cfg *timing) { u32 reg; reg = iss_reg_read(csi2->iss, csi2->regs1, CSI2_TIMING); if (timing->force_rx_mode) reg |= CSI2_TIMING_FORCE_RX_MODE_IO1; else reg &= ~CSI2_TIMING_FORCE_RX_MODE_IO1; if (timing->stop_state_16x) reg |= CSI2_TIMING_STOP_STATE_X16_IO1; else reg &= ~CSI2_TIMING_STOP_STATE_X16_IO1; if (timing->stop_state_4x) reg |= CSI2_TIMING_STOP_STATE_X4_IO1; else reg &= ~CSI2_TIMING_STOP_STATE_X4_IO1; reg &= ~CSI2_TIMING_STOP_STATE_COUNTER_IO1_MASK; reg |= timing->stop_state_counter << CSI2_TIMING_STOP_STATE_COUNTER_IO1_SHIFT; iss_reg_write(csi2->iss, csi2->regs1, CSI2_TIMING, reg); }
/* * csi2_ctx_enable - Enable specified CSI2 context * @ctxnum: Context number, valid between 0 and 7 values. * @enable: enable * */ static void csi2_ctx_enable(struct iss_csi2_device *csi2, u8 ctxnum, u8 enable) { struct iss_csi2_ctx_cfg *ctx = &csi2->contexts[ctxnum]; u32 reg; reg = iss_reg_read(csi2->iss, csi2->regs1, CSI2_CTX_CTRL1(ctxnum)); if (enable) { unsigned int skip = 0; if (csi2->frame_skip) skip = csi2->frame_skip; else if (csi2->output & CSI2_OUTPUT_MEMORY) skip = 1; reg &= ~CSI2_CTX_CTRL1_COUNT_MASK; reg |= CSI2_CTX_CTRL1_COUNT_UNLOCK | (skip << CSI2_CTX_CTRL1_COUNT_SHIFT) | CSI2_CTX_CTRL1_CTX_EN; } else { reg &= ~CSI2_CTX_CTRL1_CTX_EN; } iss_reg_write(csi2->iss, csi2->regs1, CSI2_CTX_CTRL1(ctxnum), reg); ctx->enabled = enable; }
static int __iss_subclk_update(struct iss_device *iss) { u32 clk = 0; int ret = 0, timeout = 1000; if (iss->subclk_resources & OMAP4_ISS_SUBCLK_CSI2_A) clk |= ISS_CLKCTRL_CSI2_A; if (iss->subclk_resources & OMAP4_ISS_SUBCLK_CSI2_B) clk |= ISS_CLKCTRL_CSI2_B; if (iss->subclk_resources & OMAP4_ISS_SUBCLK_ISP) clk |= ISS_CLKCTRL_ISP; iss_reg_update(iss, OMAP4_ISS_MEM_TOP, ISS_CLKCTRL, ISS_CLKCTRL_MASK, clk); /* Wait for HW assertion */ while (--timeout > 0) { udelay(1); if ((iss_reg_read(iss, OMAP4_ISS_MEM_TOP, ISS_CLKSTAT) & ISS_CLKCTRL_MASK) == clk) break; } if (!timeout) ret = -EBUSY; return ret; }
/* * omap4iss_csi2_reset - Resets the CSI2 module. * * Must be called with the phy lock held. * * Returns 0 if successful, or -EBUSY if power command didn't respond. */ int omap4iss_csi2_reset(struct iss_csi2_device *csi2) { unsigned int timeout; if (!csi2->available) return -ENODEV; if (csi2->phy->phy_in_use) return -EBUSY; iss_reg_set(csi2->iss, csi2->regs1, CSI2_SYSCONFIG, CSI2_SYSCONFIG_SOFT_RESET); timeout = iss_poll_condition_timeout( iss_reg_read(csi2->iss, csi2->regs1, CSI2_SYSSTATUS) & CSI2_SYSSTATUS_RESET_DONE, 500, 100, 200); if (timeout) { dev_err(csi2->iss->dev, "CSI2: Soft reset timeout!\n"); return -EBUSY; } iss_reg_set(csi2->iss, csi2->regs1, CSI2_COMPLEXIO_CFG, CSI2_COMPLEXIO_CFG_RESET_CTRL); timeout = iss_poll_condition_timeout( iss_reg_read(csi2->iss, csi2->phy->phy_regs, REGISTER1) & REGISTER1_RESET_DONE_CTRLCLK, 10000, 100, 500); if (timeout) { dev_err(csi2->iss->dev, "CSI2: CSI2_96M_FCLK reset timeout!\n"); return -EBUSY; } iss_reg_update(csi2->iss, csi2->regs1, CSI2_SYSCONFIG, CSI2_SYSCONFIG_MSTANDBY_MODE_MASK | CSI2_SYSCONFIG_AUTO_IDLE, CSI2_SYSCONFIG_MSTANDBY_MODE_NO); return 0; }
static int iss_reset(struct iss_device *iss) { unsigned int timeout; iss_reg_set(iss, OMAP4_ISS_MEM_TOP, ISS_HL_SYSCONFIG, ISS_HL_SYSCONFIG_SOFTRESET); timeout = iss_poll_condition_timeout( !(iss_reg_read(iss, OMAP4_ISS_MEM_TOP, ISS_HL_SYSCONFIG) & ISS_HL_SYSCONFIG_SOFTRESET), 1000, 10, 100); if (timeout) { dev_err(iss->dev, "ISS reset timeout\n"); return -ETIMEDOUT; } iss->crashed = 0; return 0; }
static void csi2_isr_ctx(struct iss_csi2_device *csi2, struct iss_csi2_ctx_cfg *ctx) { unsigned int n = ctx->ctxnum; u32 status; status = iss_reg_read(csi2->iss, csi2->regs1, CSI2_CTX_IRQSTATUS(n)); iss_reg_write(csi2->iss, csi2->regs1, CSI2_CTX_IRQSTATUS(n), status); /* Propagate frame number */ if (status & CSI2_CTX_IRQ_FS) { struct iss_pipeline *pipe = to_iss_pipeline(&csi2->subdev.entity); if (pipe->do_propagation) atomic_inc(&pipe->frame_number); } if (!(status & CSI2_CTX_IRQ_FE)) return; /* Skip interrupts until we reach the frame skip count. The CSI2 will be * automatically disabled, as the frame skip count has been programmed * in the CSI2_CTx_CTRL1::COUNT field, so reenable it. * * It would have been nice to rely on the FRAME_NUMBER interrupt instead * but it turned out that the interrupt is only generated when the CSI2 * writes to memory (the CSI2_CTx_CTRL1::COUNT field is decreased * correctly and reaches 0 when data is forwarded to the video port only * but no interrupt arrives). Maybe a CSI2 hardware bug. */ if (csi2->frame_skip) { csi2->frame_skip--; if (csi2->frame_skip == 0) { ctx->format_id = csi2_ctx_map_format(csi2); csi2_ctx_config(csi2, ctx); csi2_ctx_enable(csi2, n, 1); } return; } if (csi2->output & CSI2_OUTPUT_MEMORY) csi2_isr_buffer(csi2); }
/* * omap4iss_flush - Post pending L3 bus writes by doing a register readback * @iss: OMAP4 ISS device * * In order to force posting of pending writes, we need to write and * readback the same register, in this case the revision register. * * See this link for reference: * http://www.mail-archive.com/[email protected]/msg08149.html */ void omap4iss_flush(struct iss_device *iss) { iss_reg_write(iss, OMAP4_ISS_MEM_TOP, ISS_HL_REVISION, 0); iss_reg_read(iss, OMAP4_ISS_MEM_TOP, ISS_HL_REVISION); }
static int iss_probe(struct platform_device *pdev) { struct iss_platform_data *pdata = pdev->dev.platform_data; struct iss_device *iss; unsigned int i; int ret; if (!pdata) return -EINVAL; iss = devm_kzalloc(&pdev->dev, sizeof(*iss), GFP_KERNEL); if (!iss) return -ENOMEM; mutex_init(&iss->iss_mutex); iss->dev = &pdev->dev; iss->pdata = pdata; iss->raw_dmamask = DMA_BIT_MASK(32); iss->dev->dma_mask = &iss->raw_dmamask; iss->dev->coherent_dma_mask = DMA_BIT_MASK(32); platform_set_drvdata(pdev, iss); /* * TODO: When implementing DT support switch to syscon regmap lookup by * phandle. */ iss->syscon = syscon_regmap_lookup_by_compatible("syscon"); if (IS_ERR(iss->syscon)) { ret = PTR_ERR(iss->syscon); goto error; } /* Clocks */ ret = iss_map_mem_resource(pdev, iss, OMAP4_ISS_MEM_TOP); if (ret < 0) goto error; ret = iss_get_clocks(iss); if (ret < 0) goto error; if (!omap4iss_get(iss)) goto error; ret = iss_reset(iss); if (ret < 0) goto error_iss; iss->revision = iss_reg_read(iss, OMAP4_ISS_MEM_TOP, ISS_HL_REVISION); dev_info(iss->dev, "Revision %08x found\n", iss->revision); for (i = 1; i < OMAP4_ISS_MEM_LAST; i++) { ret = iss_map_mem_resource(pdev, iss, i); if (ret) goto error_iss; } /* Configure BTE BW_LIMITER field to max recommended value (1 GB) */ iss_reg_update(iss, OMAP4_ISS_MEM_BTE, BTE_CTRL, BTE_CTRL_BW_LIMITER_MASK, 18 << BTE_CTRL_BW_LIMITER_SHIFT); /* Perform ISP reset */ ret = omap4iss_subclk_enable(iss, OMAP4_ISS_SUBCLK_ISP); if (ret < 0) goto error_iss; ret = iss_isp_reset(iss); if (ret < 0) goto error_iss; dev_info(iss->dev, "ISP Revision %08x found\n", iss_reg_read(iss, OMAP4_ISS_MEM_ISP_SYS1, ISP5_REVISION)); /* Interrupt */ ret = platform_get_irq(pdev, 0); if (ret <= 0) { dev_err(iss->dev, "No IRQ resource\n"); ret = -ENODEV; goto error_iss; } iss->irq_num = ret; if (devm_request_irq(iss->dev, iss->irq_num, iss_isr, IRQF_SHARED, "OMAP4 ISS", iss)) { dev_err(iss->dev, "Unable to request IRQ\n"); ret = -EINVAL; goto error_iss; } /* Entities */ ret = iss_initialize_modules(iss); if (ret < 0) goto error_iss; ret = iss_register_entities(iss); if (ret < 0) goto error_modules; ret = media_entity_enum_init(&iss->crashed, &iss->media_dev); if (ret) goto error_entities; ret = iss_create_links(iss); if (ret < 0) goto error_entities; omap4iss_put(iss); return 0; error_entities: iss_unregister_entities(iss); media_entity_enum_cleanup(&iss->crashed); error_modules: iss_cleanup_modules(iss); error_iss: omap4iss_put(iss); error: platform_set_drvdata(pdev, NULL); mutex_destroy(&iss->iss_mutex); return ret; }
static int iss_probe(struct platform_device *pdev) { struct iss_platform_data *pdata = pdev->dev.platform_data; struct iss_device *iss; unsigned int i; int ret; if (pdata == NULL) return -EINVAL; iss = devm_kzalloc(&pdev->dev, sizeof(*iss), GFP_KERNEL); if (!iss) { dev_err(&pdev->dev, "Could not allocate memory\n"); return -ENOMEM; } mutex_init(&iss->iss_mutex); iss->dev = &pdev->dev; iss->pdata = pdata; iss->raw_dmamask = DMA_BIT_MASK(32); iss->dev->dma_mask = &iss->raw_dmamask; iss->dev->coherent_dma_mask = DMA_BIT_MASK(32); platform_set_drvdata(pdev, iss); /* Clocks */ ret = iss_map_mem_resource(pdev, iss, OMAP4_ISS_MEM_TOP); if (ret < 0) goto error; ret = iss_get_clocks(iss); if (ret < 0) goto error; if (omap4iss_get(iss) == NULL) goto error; ret = iss_reset(iss); if (ret < 0) goto error_iss; iss->revision = iss_reg_read(iss, OMAP4_ISS_MEM_TOP, ISS_HL_REVISION); dev_info(iss->dev, "Revision %08x found\n", iss->revision); for (i = 1; i < OMAP4_ISS_MEM_LAST; i++) { ret = iss_map_mem_resource(pdev, iss, i); if (ret) goto error_iss; } /* Configure BTE BW_LIMITER field to max recommended value (1 GB) */ iss_reg_update(iss, OMAP4_ISS_MEM_BTE, BTE_CTRL, BTE_CTRL_BW_LIMITER_MASK, 18 << BTE_CTRL_BW_LIMITER_SHIFT); /* Perform ISP reset */ ret = omap4iss_subclk_enable(iss, OMAP4_ISS_SUBCLK_ISP); if (ret < 0) goto error_iss; ret = iss_isp_reset(iss); if (ret < 0) goto error_iss; dev_info(iss->dev, "ISP Revision %08x found\n", iss_reg_read(iss, OMAP4_ISS_MEM_ISP_SYS1, ISP5_REVISION)); /* Interrupt */ iss->irq_num = platform_get_irq(pdev, 0); if (iss->irq_num <= 0) { dev_err(iss->dev, "No IRQ resource\n"); ret = -ENODEV; goto error_iss; } if (devm_request_irq(iss->dev, iss->irq_num, iss_isr, IRQF_SHARED, "OMAP4 ISS", iss)) { dev_err(iss->dev, "Unable to request IRQ\n"); ret = -EINVAL; goto error_iss; } /* Entities */ ret = iss_initialize_modules(iss); if (ret < 0) goto error_iss; ret = iss_register_entities(iss); if (ret < 0) goto error_modules; omap4iss_put(iss); return 0; error_modules: iss_cleanup_modules(iss); error_iss: omap4iss_put(iss); error: platform_set_drvdata(pdev, NULL); mutex_destroy(&iss->iss_mutex); return ret; }