diff options
Diffstat (limited to 'drivers/media/platform/renesas/rzg2l-cru')
-rw-r--r-- | drivers/media/platform/renesas/rzg2l-cru/rzg2l-core.c | 139 | ||||
-rw-r--r-- | drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru-regs.h | 91 | ||||
-rw-r--r-- | drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru.h | 39 | ||||
-rw-r--r-- | drivers/media/platform/renesas/rzg2l-cru/rzg2l-csi2.c | 165 | ||||
-rw-r--r-- | drivers/media/platform/renesas/rzg2l-cru/rzg2l-ip.c | 13 | ||||
-rw-r--r-- | drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c | 295 |
6 files changed, 636 insertions, 106 deletions
diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-core.c b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-core.c index 89be584a4988..5fa73ab2db53 100644 --- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-core.c +++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-core.c @@ -22,6 +22,7 @@ #include <media/v4l2-mc.h> #include "rzg2l-cru.h" +#include "rzg2l-cru-regs.h" static inline struct rzg2l_cru_dev *notifier_to_cru(struct v4l2_async_notifier *n) { @@ -240,10 +241,11 @@ static int rzg2l_cru_media_init(struct rzg2l_cru_dev *cru) static int rzg2l_cru_probe(struct platform_device *pdev) { + struct device *dev = &pdev->dev; struct rzg2l_cru_dev *cru; int irq, ret; - cru = devm_kzalloc(&pdev->dev, sizeof(*cru), GFP_KERNEL); + cru = devm_kzalloc(dev, sizeof(*cru), GFP_KERNEL); if (!cru) return -ENOMEM; @@ -251,32 +253,32 @@ static int rzg2l_cru_probe(struct platform_device *pdev) if (IS_ERR(cru->base)) return PTR_ERR(cru->base); - cru->presetn = devm_reset_control_get_shared(&pdev->dev, "presetn"); + cru->presetn = devm_reset_control_get_shared(dev, "presetn"); if (IS_ERR(cru->presetn)) - return dev_err_probe(&pdev->dev, PTR_ERR(cru->presetn), + return dev_err_probe(dev, PTR_ERR(cru->presetn), "Failed to get cpg presetn\n"); - cru->aresetn = devm_reset_control_get_exclusive(&pdev->dev, "aresetn"); + cru->aresetn = devm_reset_control_get_exclusive(dev, "aresetn"); if (IS_ERR(cru->aresetn)) - return dev_err_probe(&pdev->dev, PTR_ERR(cru->aresetn), + return dev_err_probe(dev, PTR_ERR(cru->aresetn), "Failed to get cpg aresetn\n"); - cru->vclk = devm_clk_get(&pdev->dev, "video"); + cru->vclk = devm_clk_get(dev, "video"); if (IS_ERR(cru->vclk)) - return dev_err_probe(&pdev->dev, PTR_ERR(cru->vclk), + return dev_err_probe(dev, PTR_ERR(cru->vclk), "Failed to get video clock\n"); - cru->dev = &pdev->dev; - cru->info = of_device_get_match_data(&pdev->dev); + cru->dev = dev; + cru->info = of_device_get_match_data(dev); irq = platform_get_irq(pdev, 0); if (irq < 0) return irq; - ret = devm_request_irq(&pdev->dev, irq, rzg2l_cru_irq, 0, + ret = devm_request_irq(dev, irq, cru->info->irq_handler, 0, KBUILD_MODNAME, cru); if (ret) - return dev_err_probe(&pdev->dev, ret, "failed to request irq\n"); + return dev_err_probe(dev, ret, "failed to request irq\n"); platform_set_drvdata(pdev, cru); @@ -285,8 +287,10 @@ static int rzg2l_cru_probe(struct platform_device *pdev) return ret; cru->num_buf = RZG2L_CRU_HW_BUFFER_DEFAULT; - pm_suspend_ignore_children(&pdev->dev, true); - pm_runtime_enable(&pdev->dev); + pm_suspend_ignore_children(dev, true); + ret = devm_pm_runtime_enable(dev); + if (ret) + goto error_dma_unregister; ret = rzg2l_cru_media_init(cru); if (ret) @@ -296,7 +300,6 @@ static int rzg2l_cru_probe(struct platform_device *pdev) error_dma_unregister: rzg2l_cru_dma_unregister(cru); - pm_runtime_disable(&pdev->dev); return ret; } @@ -305,8 +308,6 @@ static void rzg2l_cru_remove(struct platform_device *pdev) { struct rzg2l_cru_dev *cru = platform_get_drvdata(pdev); - pm_runtime_disable(&pdev->dev); - v4l2_async_nf_unregister(&cru->notifier); v4l2_async_nf_cleanup(&cru->notifier); @@ -317,8 +318,112 @@ static void rzg2l_cru_remove(struct platform_device *pdev) rzg2l_cru_dma_unregister(cru); } +static const u16 rzg3e_cru_regs[] = { + [CRUnCTRL] = 0x0, + [CRUnIE] = 0x4, + [CRUnIE2] = 0x8, + [CRUnINTS] = 0xc, + [CRUnINTS2] = 0x10, + [CRUnRST] = 0x18, + [AMnMB1ADDRL] = 0x40, + [AMnMB1ADDRH] = 0x44, + [AMnMB2ADDRL] = 0x48, + [AMnMB2ADDRH] = 0x4c, + [AMnMB3ADDRL] = 0x50, + [AMnMB3ADDRH] = 0x54, + [AMnMB4ADDRL] = 0x58, + [AMnMB4ADDRH] = 0x5c, + [AMnMB5ADDRL] = 0x60, + [AMnMB5ADDRH] = 0x64, + [AMnMB6ADDRL] = 0x68, + [AMnMB6ADDRH] = 0x6c, + [AMnMB7ADDRL] = 0x70, + [AMnMB7ADDRH] = 0x74, + [AMnMB8ADDRL] = 0x78, + [AMnMB8ADDRH] = 0x7c, + [AMnMBVALID] = 0x88, + [AMnMADRSL] = 0x8c, + [AMnMADRSH] = 0x90, + [AMnAXIATTR] = 0xec, + [AMnFIFOPNTR] = 0xf8, + [AMnAXISTP] = 0x110, + [AMnAXISTPACK] = 0x114, + [AMnIS] = 0x128, + [ICnEN] = 0x1f0, + [ICnSVCNUM] = 0x1f8, + [ICnSVC] = 0x1fc, + [ICnIPMC_C0] = 0x200, + [ICnMS] = 0x2d8, + [ICnDMR] = 0x304, +}; + +static const struct rzg2l_cru_info rzg3e_cru_info = { + .max_width = 4095, + .max_height = 4095, + .image_conv = ICnIPMC_C0, + .has_stride = true, + .regs = rzg3e_cru_regs, + .irq_handler = rzg3e_cru_irq, + .enable_interrupts = rzg3e_cru_enable_interrupts, + .disable_interrupts = rzg3e_cru_disable_interrupts, + .fifo_empty = rz3e_fifo_empty, + .csi_setup = rzg3e_cru_csi2_setup, +}; + +static const u16 rzg2l_cru_regs[] = { + [CRUnCTRL] = 0x0, + [CRUnIE] = 0x4, + [CRUnINTS] = 0x8, + [CRUnRST] = 0xc, + [AMnMB1ADDRL] = 0x100, + [AMnMB1ADDRH] = 0x104, + [AMnMB2ADDRL] = 0x108, + [AMnMB2ADDRH] = 0x10c, + [AMnMB3ADDRL] = 0x110, + [AMnMB3ADDRH] = 0x114, + [AMnMB4ADDRL] = 0x118, + [AMnMB4ADDRH] = 0x11c, + [AMnMB5ADDRL] = 0x120, + [AMnMB5ADDRH] = 0x124, + [AMnMB6ADDRL] = 0x128, + [AMnMB6ADDRH] = 0x12c, + [AMnMB7ADDRL] = 0x130, + [AMnMB7ADDRH] = 0x134, + [AMnMB8ADDRL] = 0x138, + [AMnMB8ADDRH] = 0x13c, + [AMnMBVALID] = 0x148, + [AMnMBS] = 0x14c, + [AMnAXIATTR] = 0x158, + [AMnFIFOPNTR] = 0x168, + [AMnAXISTP] = 0x174, + [AMnAXISTPACK] = 0x178, + [ICnEN] = 0x200, + [ICnMC] = 0x208, + [ICnMS] = 0x254, + [ICnDMR] = 0x26c, +}; + +static const struct rzg2l_cru_info rzgl2_cru_info = { + .max_width = 2800, + .max_height = 4095, + .image_conv = ICnMC, + .regs = rzg2l_cru_regs, + .irq_handler = rzg2l_cru_irq, + .enable_interrupts = rzg2l_cru_enable_interrupts, + .disable_interrupts = rzg2l_cru_disable_interrupts, + .fifo_empty = rzg2l_fifo_empty, + .csi_setup = rzg2l_cru_csi2_setup, +}; + static const struct of_device_id rzg2l_cru_of_id_table[] = { - { .compatible = "renesas,rzg2l-cru", }, + { + .compatible = "renesas,r9a09g047-cru", + .data = &rzg3e_cru_info, + }, + { + .compatible = "renesas,rzg2l-cru", + .data = &rzgl2_cru_info, + }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, rzg2l_cru_of_id_table); diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru-regs.h b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru-regs.h index 1c9f22118a5d..a5a57369ef0e 100644 --- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru-regs.h +++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru-regs.h @@ -10,71 +10,102 @@ /* HW CRU Registers Definition */ -/* CRU Control Register */ -#define CRUnCTRL 0x0 #define CRUnCTRL_VINSEL(x) ((x) << 0) -/* CRU Interrupt Enable Register */ -#define CRUnIE 0x4 #define CRUnIE_EFE BIT(17) -/* CRU Interrupt Status Register */ -#define CRUnINTS 0x8 +#define CRUnIE2_FSxE(x) BIT(((x) * 3)) +#define CRUnIE2_FExE(x) BIT(((x) * 3) + 1) + #define CRUnINTS_SFS BIT(16) -/* CRU Reset Register */ -#define CRUnRST 0xc +#define CRUnINTS2_FSxS(x) BIT(((x) * 3)) + #define CRUnRST_VRESETN BIT(0) /* Memory Bank Base Address (Lower) Register for CRU Image Data */ -#define AMnMBxADDRL(x) (0x100 + ((x) * 8)) +#define AMnMBxADDRL(x) (AMnMB1ADDRL + (x) * 2) /* Memory Bank Base Address (Higher) Register for CRU Image Data */ -#define AMnMBxADDRH(x) (0x104 + ((x) * 8)) +#define AMnMBxADDRH(x) (AMnMB1ADDRH + (x) * 2) -/* Memory Bank Enable Register for CRU Image Data */ -#define AMnMBVALID 0x148 #define AMnMBVALID_MBVALID(x) GENMASK(x, 0) -/* Memory Bank Status Register for CRU Image Data */ -#define AMnMBS 0x14c #define AMnMBS_MBSTS 0x7 -/* AXI Master Transfer Setting Register for CRU Image Data */ -#define AMnAXIATTR 0x158 #define AMnAXIATTR_AXILEN_MASK GENMASK(3, 0) #define AMnAXIATTR_AXILEN (0xf) -/* AXI Master FIFO Pointer Register for CRU Image Data */ -#define AMnFIFOPNTR 0x168 #define AMnFIFOPNTR_FIFOWPNTR GENMASK(7, 0) +#define AMnFIFOPNTR_FIFOWPNTR_B0 AMnFIFOPNTR_FIFOWPNTR +#define AMnFIFOPNTR_FIFOWPNTR_B1 GENMASK(15, 8) #define AMnFIFOPNTR_FIFORPNTR_Y GENMASK(23, 16) +#define AMnFIFOPNTR_FIFORPNTR_B0 AMnFIFOPNTR_FIFORPNTR_Y +#define AMnFIFOPNTR_FIFORPNTR_B1 GENMASK(31, 24) + +#define AMnIS_IS_MASK GENMASK(14, 7) +#define AMnIS_IS(x) ((x) << 7) -/* AXI Master Transfer Stop Register for CRU Image Data */ -#define AMnAXISTP 0x174 #define AMnAXISTP_AXI_STOP BIT(0) -/* AXI Master Transfer Stop Status Register for CRU Image Data */ -#define AMnAXISTPACK 0x178 #define AMnAXISTPACK_AXI_STOP_ACK BIT(0) -/* CRU Image Processing Enable Register */ -#define ICnEN 0x200 #define ICnEN_ICEN BIT(0) -/* CRU Image Processing Main Control Register */ -#define ICnMC 0x208 +#define ICnSVC_SVC0(x) (x) +#define ICnSVC_SVC1(x) ((x) << 4) +#define ICnSVC_SVC2(x) ((x) << 8) +#define ICnSVC_SVC3(x) ((x) << 12) + #define ICnMC_CSCTHR BIT(5) #define ICnMC_INF(x) ((x) << 16) #define ICnMC_VCSEL(x) ((x) << 22) #define ICnMC_INF_MASK GENMASK(21, 16) -/* CRU Module Status Register */ -#define ICnMS 0x254 #define ICnMS_IA BIT(2) -/* CRU Data Output Mode Register */ -#define ICnDMR 0x26c #define ICnDMR_YCMODE_UYVY (1 << 4) +enum rzg2l_cru_common_regs { + CRUnCTRL, /* CRU Control */ + CRUnIE, /* CRU Interrupt Enable */ + CRUnIE2, /* CRU Interrupt Enable(2) */ + CRUnINTS, /* CRU Interrupt Status */ + CRUnINTS2, /* CRU Interrupt Status(2) */ + CRUnRST, /* CRU Reset */ + AMnMB1ADDRL, /* Bank 1 Address (Lower) for CRU Image Data */ + AMnMB1ADDRH, /* Bank 1 Address (Higher) for CRU Image Data */ + AMnMB2ADDRL, /* Bank 2 Address (Lower) for CRU Image Data */ + AMnMB2ADDRH, /* Bank 2 Address (Higher) for CRU Image Data */ + AMnMB3ADDRL, /* Bank 3 Address (Lower) for CRU Image Data */ + AMnMB3ADDRH, /* Bank 3 Address (Higher) for CRU Image Data */ + AMnMB4ADDRL, /* Bank 4 Address (Lower) for CRU Image Data */ + AMnMB4ADDRH, /* Bank 4 Address (Higher) for CRU Image Data */ + AMnMB5ADDRL, /* Bank 5 Address (Lower) for CRU Image Data */ + AMnMB5ADDRH, /* Bank 5 Address (Higher) for CRU Image Data */ + AMnMB6ADDRL, /* Bank 6 Address (Lower) for CRU Image Data */ + AMnMB6ADDRH, /* Bank 6 Address (Higher) for CRU Image Data */ + AMnMB7ADDRL, /* Bank 7 Address (Lower) for CRU Image Data */ + AMnMB7ADDRH, /* Bank 7 Address (Higher) for CRU Image Data */ + AMnMB8ADDRL, /* Bank 8 Address (Lower) for CRU Image Data */ + AMnMB8ADDRH, /* Bank 8 Address (Higher) for CRU Image Data */ + AMnMBVALID, /* Memory Bank Enable for CRU Image Data */ + AMnMBS, /* Memory Bank Status for CRU Image Data */ + AMnMADRSL, /* VD Memory Address Lower Status Register */ + AMnMADRSH, /* VD Memory Address Higher Status Register */ + AMnAXIATTR, /* AXI Master Transfer Setting Register for CRU Image Data */ + AMnFIFOPNTR, /* AXI Master FIFO Pointer for CRU Image Data */ + AMnAXISTP, /* AXI Master Transfer Stop for CRU Image Data */ + AMnAXISTPACK, /* AXI Master Transfer Stop Status for CRU Image Data */ + AMnIS, /* Image Stride Setting Register */ + ICnEN, /* CRU Image Processing Enable */ + ICnSVCNUM, /* CRU SVC Number Register */ + ICnSVC, /* CRU VC Select Register */ + ICnMC, /* CRU Image Processing Main Control */ + ICnIPMC_C0, /* CRU Image Converter Main Control 0 */ + ICnMS, /* CRU Module Status */ + ICnDMR, /* CRU Data Output Mode */ + RZG2L_CRU_MAX_REG, +}; + #endif /* __RZG2L_CRU_REGS_H__ */ diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru.h b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru.h index 8b898ce05b84..c30f3b281284 100644 --- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru.h +++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru.h @@ -27,15 +27,15 @@ #define RZG2L_CRU_CSI2_VCHANNEL 4 #define RZG2L_CRU_MIN_INPUT_WIDTH 320 -#define RZG2L_CRU_MAX_INPUT_WIDTH 2800 #define RZG2L_CRU_MIN_INPUT_HEIGHT 240 -#define RZG2L_CRU_MAX_INPUT_HEIGHT 4095 enum rzg2l_csi2_pads { RZG2L_CRU_IP_SINK = 0, RZG2L_CRU_IP_SOURCE, }; +struct rzg2l_cru_dev; + /** * enum rzg2l_cru_dma_state - DMA states * @RZG2L_CRU_DMA_STOPPED: No operation in progress @@ -80,6 +80,21 @@ struct rzg2l_cru_ip_format { bool yuv; }; +struct rzg2l_cru_info { + unsigned int max_width; + unsigned int max_height; + u16 image_conv; + const u16 *regs; + bool has_stride; + irqreturn_t (*irq_handler)(int irq, void *data); + void (*enable_interrupts)(struct rzg2l_cru_dev *cru); + void (*disable_interrupts)(struct rzg2l_cru_dev *cru); + bool (*fifo_empty)(struct rzg2l_cru_dev *cru); + void (*csi_setup)(struct rzg2l_cru_dev *cru, + const struct rzg2l_cru_ip_format *ip_fmt, + u8 csi_vc); +}; + /** * struct rzg2l_cru_dev - Renesas CRU device structure * @dev: (OF) device @@ -94,6 +109,8 @@ struct rzg2l_cru_ip_format { * @vdev: V4L2 video device associated with CRU * @v4l2_dev: V4L2 device * @num_buf: Holds the current number of buffers enabled + * @svc_channel: SVC0/1/2/3 to use for RZ/G3E + * @buf_addr: Memory addresses where current video data is written. * @notifier: V4L2 asynchronous subdevs notifier * * @ip: Image processing subdev info @@ -130,6 +147,9 @@ struct rzg2l_cru_dev { struct v4l2_device v4l2_dev; u8 num_buf; + u8 svc_channel; + dma_addr_t buf_addr[RZG2L_CRU_HW_BUFFER_DEFAULT]; + struct v4l2_async_notifier notifier; struct rzg2l_cru_ip ip; @@ -161,6 +181,7 @@ void rzg2l_cru_dma_unregister(struct rzg2l_cru_dev *cru); int rzg2l_cru_video_register(struct rzg2l_cru_dev *cru); void rzg2l_cru_video_unregister(struct rzg2l_cru_dev *cru); irqreturn_t rzg2l_cru_irq(int irq, void *data); +irqreturn_t rzg3e_cru_irq(int irq, void *data); const struct v4l2_format_info *rzg2l_cru_format_from_pixel(u32 format); @@ -172,4 +193,18 @@ const struct rzg2l_cru_ip_format *rzg2l_cru_ip_code_to_fmt(unsigned int code); const struct rzg2l_cru_ip_format *rzg2l_cru_ip_format_to_fmt(u32 format); const struct rzg2l_cru_ip_format *rzg2l_cru_ip_index_to_fmt(u32 index); +void rzg2l_cru_enable_interrupts(struct rzg2l_cru_dev *cru); +void rzg2l_cru_disable_interrupts(struct rzg2l_cru_dev *cru); +void rzg3e_cru_enable_interrupts(struct rzg2l_cru_dev *cru); +void rzg3e_cru_disable_interrupts(struct rzg2l_cru_dev *cru); + +bool rzg2l_fifo_empty(struct rzg2l_cru_dev *cru); +bool rz3e_fifo_empty(struct rzg2l_cru_dev *cru); +void rzg2l_cru_csi2_setup(struct rzg2l_cru_dev *cru, + const struct rzg2l_cru_ip_format *ip_fmt, + u8 csi_vc); +void rzg3e_cru_csi2_setup(struct rzg2l_cru_dev *cru, + const struct rzg2l_cru_ip_format *ip_fmt, + u8 csi_vc); + #endif diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-csi2.c b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-csi2.c index 881e910dce02..9243306e2aa9 100644 --- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-csi2.c +++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-csi2.c @@ -85,6 +85,15 @@ CSIDPHYSKW0_UTIL_DL2_SKW_ADJ(1) | \ CSIDPHYSKW0_UTIL_DL3_SKW_ADJ(1)) +/* DPHY registers on RZ/V2H(P) SoC */ +#define CRUm_S_TIMCTL 0x41c +#define CRUm_S_TIMCTL_S_HSSETTLECTL(x) ((x) << 8) + +#define CRUm_S_DPHYCTL_MSB 0x434 +#define CRUm_S_DPHYCTL_MSB_DESKEW BIT(1) + +#define CRUm_SWAPCTL 0x438 + #define VSRSTS_RETRIES 20 #define RZG2L_CSI2_MIN_WIDTH 320 @@ -107,6 +116,7 @@ struct rzg2l_csi2 { void __iomem *base; struct reset_control *presetn; struct reset_control *cmn_rstb; + const struct rzg2l_csi2_info *info; struct clk *sysclk; struct clk *vclk; unsigned long vclk_rate; @@ -123,6 +133,12 @@ struct rzg2l_csi2 { bool dphy_enabled; }; +struct rzg2l_csi2_info { + int (*dphy_enable)(struct rzg2l_csi2 *csi2); + int (*dphy_disable)(struct rzg2l_csi2 *csi2); + bool has_system_clk; +}; + struct rzg2l_csi2_timings { u32 t_init; u32 tclk_miss; @@ -133,6 +149,30 @@ struct rzg2l_csi2_timings { u32 max_hsfreq; }; +struct rzv2h_csi2_s_hssettlectl { + unsigned int hsfreq; + u16 s_hssettlectl; +}; + +static const struct rzv2h_csi2_s_hssettlectl rzv2h_s_hssettlectl[] = { + { 90, 1 }, { 130, 2 }, { 180, 3 }, + { 220, 4 }, { 270, 5 }, { 310, 6 }, + { 360, 7 }, { 400, 8 }, { 450, 9 }, + { 490, 10 }, { 540, 11 }, { 580, 12 }, + { 630, 13 }, { 670, 14 }, { 720, 15 }, + { 760, 16 }, { 810, 17 }, { 850, 18 }, + { 900, 19 }, { 940, 20 }, { 990, 21 }, + { 1030, 22 }, { 1080, 23 }, { 1120, 24 }, + { 1170, 25 }, { 1220, 26 }, { 1260, 27 }, + { 1310, 28 }, { 1350, 29 }, { 1400, 30 }, + { 1440, 31 }, { 1490, 32 }, { 1530, 33 }, + { 1580, 34 }, { 1620, 35 }, { 1670, 36 }, + { 1710, 37 }, { 1760, 38 }, { 1800, 39 }, + { 1850, 40 }, { 1890, 41 }, { 1940, 42 }, + { 1980, 43 }, { 2030, 44 }, { 2070, 45 }, + { 2100, 46 }, +}; + static const struct rzg2l_csi2_timings rzg2l_csi2_global_timings[] = { { .max_hsfreq = 80, @@ -355,14 +395,20 @@ static int rzg2l_csi2_dphy_enable(struct rzg2l_csi2 *csi2) return ret; } +static const struct rzg2l_csi2_info rzg2l_csi2_info = { + .dphy_enable = rzg2l_csi2_dphy_enable, + .dphy_disable = rzg2l_csi2_dphy_disable, + .has_system_clk = true, +}; + static int rzg2l_csi2_dphy_setting(struct v4l2_subdev *sd, bool on) { struct rzg2l_csi2 *csi2 = sd_to_csi2(sd); if (on) - return rzg2l_csi2_dphy_enable(csi2); + return csi2->info->dphy_enable(csi2); - return rzg2l_csi2_dphy_disable(csi2); + return csi2->info->dphy_disable(csi2); } static int rzg2l_csi2_mipi_link_enable(struct rzg2l_csi2 *csi2) @@ -421,6 +467,64 @@ static int rzg2l_csi2_mipi_link_disable(struct rzg2l_csi2 *csi2) return 0; } +static int rzv2h_csi2_dphy_disable(struct rzg2l_csi2 *csi2) +{ + int ret; + + /* Reset the CRU (D-PHY) */ + ret = reset_control_assert(csi2->cmn_rstb); + if (ret) + return ret; + + csi2->dphy_enabled = false; + + return 0; +} + +static int rzv2h_csi2_dphy_enable(struct rzg2l_csi2 *csi2) +{ + unsigned int i; + u16 hssettle; + int mbps; + + mbps = rzg2l_csi2_calc_mbps(csi2); + if (mbps < 0) + return mbps; + + csi2->hsfreq = mbps; + + for (i = 0; i < ARRAY_SIZE(rzv2h_s_hssettlectl); i++) { + if (csi2->hsfreq <= rzv2h_s_hssettlectl[i].hsfreq) + break; + } + + if (i == ARRAY_SIZE(rzv2h_s_hssettlectl)) + return -EINVAL; + + rzg2l_csi2_write(csi2, CRUm_SWAPCTL, 0); + + hssettle = rzv2h_s_hssettlectl[i].s_hssettlectl; + rzg2l_csi2_write(csi2, CRUm_S_TIMCTL, + CRUm_S_TIMCTL_S_HSSETTLECTL(hssettle)); + + if (csi2->hsfreq > 1500) + rzg2l_csi2_set(csi2, CRUm_S_DPHYCTL_MSB, + CRUm_S_DPHYCTL_MSB_DESKEW); + else + rzg2l_csi2_clr(csi2, CRUm_S_DPHYCTL_MSB, + CRUm_S_DPHYCTL_MSB_DESKEW); + + csi2->dphy_enabled = true; + + return 0; +} + +static const struct rzg2l_csi2_info rzv2h_csi2_info = { + .dphy_enable = rzv2h_csi2_dphy_enable, + .dphy_disable = rzv2h_csi2_dphy_disable, + .has_system_clk = false, +}; + static int rzg2l_csi2_mipi_link_setting(struct v4l2_subdev *sd, bool on) { struct rzg2l_csi2 *csi2 = sd_to_csi2(sd); @@ -764,39 +868,46 @@ static const struct media_entity_operations rzg2l_csi2_entity_ops = { static int rzg2l_csi2_probe(struct platform_device *pdev) { + struct device *dev = &pdev->dev; struct rzg2l_csi2 *csi2; int ret; - csi2 = devm_kzalloc(&pdev->dev, sizeof(*csi2), GFP_KERNEL); + csi2 = devm_kzalloc(dev, sizeof(*csi2), GFP_KERNEL); if (!csi2) return -ENOMEM; + csi2->info = of_device_get_match_data(dev); + if (!csi2->info) + return dev_err_probe(dev, -EINVAL, "Failed to get OF match data\n"); + csi2->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(csi2->base)) return PTR_ERR(csi2->base); - csi2->cmn_rstb = devm_reset_control_get_exclusive(&pdev->dev, "cmn-rstb"); + csi2->cmn_rstb = devm_reset_control_get_exclusive(dev, "cmn-rstb"); if (IS_ERR(csi2->cmn_rstb)) - return dev_err_probe(&pdev->dev, PTR_ERR(csi2->cmn_rstb), + return dev_err_probe(dev, PTR_ERR(csi2->cmn_rstb), "Failed to get cpg cmn-rstb\n"); - csi2->presetn = devm_reset_control_get_shared(&pdev->dev, "presetn"); + csi2->presetn = devm_reset_control_get_shared(dev, "presetn"); if (IS_ERR(csi2->presetn)) - return dev_err_probe(&pdev->dev, PTR_ERR(csi2->presetn), + return dev_err_probe(dev, PTR_ERR(csi2->presetn), "Failed to get cpg presetn\n"); - csi2->sysclk = devm_clk_get(&pdev->dev, "system"); - if (IS_ERR(csi2->sysclk)) - return dev_err_probe(&pdev->dev, PTR_ERR(csi2->sysclk), - "Failed to get system clk\n"); + if (csi2->info->has_system_clk) { + csi2->sysclk = devm_clk_get(dev, "system"); + if (IS_ERR(csi2->sysclk)) + return dev_err_probe(dev, PTR_ERR(csi2->sysclk), + "Failed to get system clk\n"); + } - csi2->vclk = devm_clk_get(&pdev->dev, "video"); + csi2->vclk = devm_clk_get(dev, "video"); if (IS_ERR(csi2->vclk)) - return dev_err_probe(&pdev->dev, PTR_ERR(csi2->vclk), + return dev_err_probe(dev, PTR_ERR(csi2->vclk), "Failed to get video clock\n"); csi2->vclk_rate = clk_get_rate(csi2->vclk); - csi2->dev = &pdev->dev; + csi2->dev = dev; platform_set_drvdata(pdev, csi2); @@ -804,18 +915,20 @@ static int rzg2l_csi2_probe(struct platform_device *pdev) if (ret) return ret; - pm_runtime_enable(&pdev->dev); + ret = devm_pm_runtime_enable(dev); + if (ret) + return ret; ret = rzg2l_validate_csi2_lanes(csi2); if (ret) - goto error_pm; + return ret; - csi2->subdev.dev = &pdev->dev; + csi2->subdev.dev = dev; v4l2_subdev_init(&csi2->subdev, &rzg2l_csi2_subdev_ops); csi2->subdev.internal_ops = &rzg2l_csi2_internal_ops; - v4l2_set_subdevdata(&csi2->subdev, &pdev->dev); + v4l2_set_subdevdata(&csi2->subdev, dev); snprintf(csi2->subdev.name, sizeof(csi2->subdev.name), - "csi-%s", dev_name(&pdev->dev)); + "csi-%s", dev_name(dev)); csi2->subdev.flags = V4L2_SUBDEV_FL_HAS_DEVNODE; csi2->subdev.entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; @@ -833,7 +946,7 @@ static int rzg2l_csi2_probe(struct platform_device *pdev) ret = media_entity_pads_init(&csi2->subdev.entity, ARRAY_SIZE(csi2->pads), csi2->pads); if (ret) - goto error_pm; + return ret; ret = v4l2_subdev_init_finalize(&csi2->subdev); if (ret < 0) @@ -851,8 +964,6 @@ error_async: v4l2_async_nf_unregister(&csi2->notifier); v4l2_async_nf_cleanup(&csi2->notifier); media_entity_cleanup(&csi2->subdev.entity); -error_pm: - pm_runtime_disable(&pdev->dev); return ret; } @@ -866,7 +977,6 @@ static void rzg2l_csi2_remove(struct platform_device *pdev) v4l2_async_unregister_subdev(&csi2->subdev); v4l2_subdev_cleanup(&csi2->subdev); media_entity_cleanup(&csi2->subdev.entity); - pm_runtime_disable(&pdev->dev); } static int rzg2l_csi2_pm_runtime_suspend(struct device *dev) @@ -891,7 +1001,14 @@ static const struct dev_pm_ops rzg2l_csi2_pm_ops = { }; static const struct of_device_id rzg2l_csi2_of_table[] = { - { .compatible = "renesas,rzg2l-csi2", }, + { + .compatible = "renesas,r9a09g057-csi2", + .data = &rzv2h_csi2_info, + }, + { + .compatible = "renesas,rzg2l-csi2", + .data = &rzg2l_csi2_info, + }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, rzg2l_csi2_of_table); diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-ip.c b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-ip.c index 76a2b451f1da..7836c7cd53dc 100644 --- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-ip.c +++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-ip.c @@ -148,6 +148,8 @@ static int rzg2l_cru_ip_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_state *state, struct v4l2_subdev_format *fmt) { + struct rzg2l_cru_dev *cru = v4l2_get_subdevdata(sd); + const struct rzg2l_cru_info *info = cru->info; struct v4l2_mbus_framefmt *src_format; struct v4l2_mbus_framefmt *sink_format; @@ -170,9 +172,9 @@ static int rzg2l_cru_ip_set_format(struct v4l2_subdev *sd, sink_format->ycbcr_enc = fmt->format.ycbcr_enc; sink_format->quantization = fmt->format.quantization; sink_format->width = clamp_t(u32, fmt->format.width, - RZG2L_CRU_MIN_INPUT_WIDTH, RZG2L_CRU_MAX_INPUT_WIDTH); + RZG2L_CRU_MIN_INPUT_WIDTH, info->max_width); sink_format->height = clamp_t(u32, fmt->format.height, - RZG2L_CRU_MIN_INPUT_HEIGHT, RZG2L_CRU_MAX_INPUT_HEIGHT); + RZG2L_CRU_MIN_INPUT_HEIGHT, info->max_height); fmt->format = *sink_format; @@ -197,6 +199,9 @@ static int rzg2l_cru_ip_enum_frame_size(struct v4l2_subdev *sd, struct v4l2_subdev_state *state, struct v4l2_subdev_frame_size_enum *fse) { + struct rzg2l_cru_dev *cru = v4l2_get_subdevdata(sd); + const struct rzg2l_cru_info *info = cru->info; + if (fse->index != 0) return -EINVAL; @@ -205,8 +210,8 @@ static int rzg2l_cru_ip_enum_frame_size(struct v4l2_subdev *sd, fse->min_width = RZG2L_CRU_MIN_INPUT_WIDTH; fse->min_height = RZG2L_CRU_MIN_INPUT_HEIGHT; - fse->max_width = RZG2L_CRU_MAX_INPUT_WIDTH; - fse->max_height = RZG2L_CRU_MAX_INPUT_HEIGHT; + fse->max_width = info->max_width; + fse->max_height = info->max_height; return 0; } diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c index cd69c8a686d3..067c6af14e95 100644 --- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c +++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c @@ -31,6 +31,9 @@ #define RZG2L_CRU_DEFAULT_FIELD V4L2_FIELD_NONE #define RZG2L_CRU_DEFAULT_COLORSPACE V4L2_COLORSPACE_SRGB +#define RZG2L_CRU_STRIDE_MAX 32640 +#define RZG2L_CRU_STRIDE_ALIGN 128 + struct rzg2l_cru_buffer { struct vb2_v4l2_buffer vb; struct list_head list; @@ -42,16 +45,66 @@ struct rzg2l_cru_buffer { /* ----------------------------------------------------------------------------- * DMA operations */ -static void rzg2l_cru_write(struct rzg2l_cru_dev *cru, u32 offset, u32 value) +static void __rzg2l_cru_write(struct rzg2l_cru_dev *cru, u32 offset, u32 value) { - iowrite32(value, cru->base + offset); + const u16 *regs = cru->info->regs; + + /* + * CRUnCTRL is a first register on all CRU supported SoCs so validate + * rest of the registers have valid offset being set in cru->info->regs. + */ + if (WARN_ON(offset >= RZG2L_CRU_MAX_REG) || + WARN_ON(offset != CRUnCTRL && regs[offset] == 0)) + return; + + iowrite32(value, cru->base + regs[offset]); } -static u32 rzg2l_cru_read(struct rzg2l_cru_dev *cru, u32 offset) +static u32 __rzg2l_cru_read(struct rzg2l_cru_dev *cru, u32 offset) { - return ioread32(cru->base + offset); + const u16 *regs = cru->info->regs; + + /* + * CRUnCTRL is a first register on all CRU supported SoCs so validate + * rest of the registers have valid offset being set in cru->info->regs. + */ + if (WARN_ON(offset >= RZG2L_CRU_MAX_REG) || + WARN_ON(offset != CRUnCTRL && regs[offset] == 0)) + return 0; + + return ioread32(cru->base + regs[offset]); } +static __always_inline void +__rzg2l_cru_write_constant(struct rzg2l_cru_dev *cru, u32 offset, u32 value) +{ + const u16 *regs = cru->info->regs; + + BUILD_BUG_ON(offset >= RZG2L_CRU_MAX_REG); + + iowrite32(value, cru->base + regs[offset]); +} + +static __always_inline u32 +__rzg2l_cru_read_constant(struct rzg2l_cru_dev *cru, u32 offset) +{ + const u16 *regs = cru->info->regs; + + BUILD_BUG_ON(offset >= RZG2L_CRU_MAX_REG); + + return ioread32(cru->base + regs[offset]); +} + +#define rzg2l_cru_write(cru, offset, value) \ + (__builtin_constant_p(offset) ? \ + __rzg2l_cru_write_constant(cru, offset, value) : \ + __rzg2l_cru_write(cru, offset, value)) + +#define rzg2l_cru_read(cru, offset) \ + (__builtin_constant_p(offset) ? \ + __rzg2l_cru_read_constant(cru, offset) : \ + __rzg2l_cru_read(cru, offset)) + /* Need to hold qlock before calling */ static void return_unused_buffers(struct rzg2l_cru_dev *cru, enum vb2_buffer_state state) @@ -134,6 +187,8 @@ static void rzg2l_cru_set_slot_addr(struct rzg2l_cru_dev *cru, /* Currently, we just use the buffer in 32 bits address */ rzg2l_cru_write(cru, AMnMBxADDRL(slot), addr); rzg2l_cru_write(cru, AMnMBxADDRH(slot), 0); + + cru->buf_addr[slot] = addr; } /* @@ -174,6 +229,7 @@ static void rzg2l_cru_fill_hw_slot(struct rzg2l_cru_dev *cru, int slot) static void rzg2l_cru_initialize_axi(struct rzg2l_cru_dev *cru) { + const struct rzg2l_cru_info *info = cru->info; unsigned int slot; u32 amnaxiattr; @@ -186,35 +242,64 @@ static void rzg2l_cru_initialize_axi(struct rzg2l_cru_dev *cru) for (slot = 0; slot < cru->num_buf; slot++) rzg2l_cru_fill_hw_slot(cru, slot); + if (info->has_stride) { + u32 stride = cru->format.bytesperline; + u32 amnis; + + stride /= RZG2L_CRU_STRIDE_ALIGN; + amnis = rzg2l_cru_read(cru, AMnIS) & ~AMnIS_IS_MASK; + rzg2l_cru_write(cru, AMnIS, amnis | AMnIS_IS(stride)); + } + /* Set AXI burst max length to recommended setting */ amnaxiattr = rzg2l_cru_read(cru, AMnAXIATTR) & ~AMnAXIATTR_AXILEN_MASK; amnaxiattr |= AMnAXIATTR_AXILEN; rzg2l_cru_write(cru, AMnAXIATTR, amnaxiattr); } -static void rzg2l_cru_csi2_setup(struct rzg2l_cru_dev *cru, - const struct rzg2l_cru_ip_format *ip_fmt, - u8 csi_vc) +void rzg3e_cru_csi2_setup(struct rzg2l_cru_dev *cru, + const struct rzg2l_cru_ip_format *ip_fmt, + u8 csi_vc) { + const struct rzg2l_cru_info *info = cru->info; u32 icnmc = ICnMC_INF(ip_fmt->datatype); - icnmc |= (rzg2l_cru_read(cru, ICnMC) & ~ICnMC_INF_MASK); + icnmc |= rzg2l_cru_read(cru, info->image_conv) & ~ICnMC_INF_MASK; /* Set virtual channel CSI2 */ icnmc |= ICnMC_VCSEL(csi_vc); - rzg2l_cru_write(cru, ICnMC, icnmc); + rzg2l_cru_write(cru, ICnSVCNUM, csi_vc); + rzg2l_cru_write(cru, ICnSVC, ICnSVC_SVC0(0) | ICnSVC_SVC1(1) | + ICnSVC_SVC2(2) | ICnSVC_SVC3(3)); + rzg2l_cru_write(cru, info->image_conv, icnmc); +} + +void rzg2l_cru_csi2_setup(struct rzg2l_cru_dev *cru, + const struct rzg2l_cru_ip_format *ip_fmt, + u8 csi_vc) +{ + const struct rzg2l_cru_info *info = cru->info; + u32 icnmc = ICnMC_INF(ip_fmt->datatype); + + icnmc |= rzg2l_cru_read(cru, info->image_conv) & ~ICnMC_INF_MASK; + + /* Set virtual channel CSI2 */ + icnmc |= ICnMC_VCSEL(csi_vc); + + rzg2l_cru_write(cru, info->image_conv, icnmc); } static int rzg2l_cru_initialize_image_conv(struct rzg2l_cru_dev *cru, struct v4l2_mbus_framefmt *ip_sd_fmt, u8 csi_vc) { + const struct rzg2l_cru_info *info = cru->info; const struct rzg2l_cru_ip_format *cru_video_fmt; const struct rzg2l_cru_ip_format *cru_ip_fmt; cru_ip_fmt = rzg2l_cru_ip_code_to_fmt(ip_sd_fmt->code); - rzg2l_cru_csi2_setup(cru, cru_ip_fmt, csi_vc); + info->csi_setup(cru, cru_ip_fmt, csi_vc); /* Output format */ cru_video_fmt = rzg2l_cru_ip_format_to_fmt(cru->format.pixelformat); @@ -226,11 +311,11 @@ static int rzg2l_cru_initialize_image_conv(struct rzg2l_cru_dev *cru, /* If input and output use same colorspace, do bypass mode */ if (cru_ip_fmt->yuv == cru_video_fmt->yuv) - rzg2l_cru_write(cru, ICnMC, - rzg2l_cru_read(cru, ICnMC) | ICnMC_CSCTHR); + rzg2l_cru_write(cru, info->image_conv, + rzg2l_cru_read(cru, info->image_conv) | ICnMC_CSCTHR); else - rzg2l_cru_write(cru, ICnMC, - rzg2l_cru_read(cru, ICnMC) & (~ICnMC_CSCTHR)); + rzg2l_cru_write(cru, info->image_conv, + rzg2l_cru_read(cru, info->image_conv) & ~ICnMC_CSCTHR); /* Set output data format */ rzg2l_cru_write(cru, ICnDMR, cru_video_fmt->icndmr); @@ -238,9 +323,36 @@ static int rzg2l_cru_initialize_image_conv(struct rzg2l_cru_dev *cru, return 0; } -void rzg2l_cru_stop_image_processing(struct rzg2l_cru_dev *cru) +bool rz3e_fifo_empty(struct rzg2l_cru_dev *cru) +{ + u32 amnfifopntr = rzg2l_cru_read(cru, AMnFIFOPNTR); + + if ((((amnfifopntr & AMnFIFOPNTR_FIFORPNTR_B1) >> 24) == + ((amnfifopntr & AMnFIFOPNTR_FIFOWPNTR_B1) >> 8)) && + (((amnfifopntr & AMnFIFOPNTR_FIFORPNTR_B0) >> 16) == + (amnfifopntr & AMnFIFOPNTR_FIFOWPNTR_B0))) + return true; + + return false; +} + +bool rzg2l_fifo_empty(struct rzg2l_cru_dev *cru) { u32 amnfifopntr, amnfifopntr_w, amnfifopntr_r_y; + + amnfifopntr = rzg2l_cru_read(cru, AMnFIFOPNTR); + + amnfifopntr_w = amnfifopntr & AMnFIFOPNTR_FIFOWPNTR; + amnfifopntr_r_y = + (amnfifopntr & AMnFIFOPNTR_FIFORPNTR_Y) >> 16; + if (amnfifopntr_w == amnfifopntr_r_y) + return true; + + return amnfifopntr_w == amnfifopntr_r_y; +} + +void rzg2l_cru_stop_image_processing(struct rzg2l_cru_dev *cru) +{ unsigned int retries = 0; unsigned long flags; u32 icnms; @@ -248,8 +360,7 @@ void rzg2l_cru_stop_image_processing(struct rzg2l_cru_dev *cru) spin_lock_irqsave(&cru->qlock, flags); /* Disable and clear the interrupt */ - rzg2l_cru_write(cru, CRUnIE, 0); - rzg2l_cru_write(cru, CRUnINTS, 0x001F0F0F); + cru->info->disable_interrupts(cru); /* Stop the operation of image conversion */ rzg2l_cru_write(cru, ICnEN, 0); @@ -269,12 +380,7 @@ void rzg2l_cru_stop_image_processing(struct rzg2l_cru_dev *cru) /* Wait until the FIFO becomes empty */ for (retries = 5; retries > 0; retries--) { - amnfifopntr = rzg2l_cru_read(cru, AMnFIFOPNTR); - - amnfifopntr_w = amnfifopntr & AMnFIFOPNTR_FIFOWPNTR; - amnfifopntr_r_y = - (amnfifopntr & AMnFIFOPNTR_FIFORPNTR_Y) >> 16; - if (amnfifopntr_w == amnfifopntr_r_y) + if (cru->info->fifo_empty(cru)) break; usleep_range(10, 20); @@ -341,6 +447,31 @@ static int rzg2l_cru_get_virtual_channel(struct rzg2l_cru_dev *cru) return fd.entry[0].bus.csi2.vc; } +void rzg3e_cru_enable_interrupts(struct rzg2l_cru_dev *cru) +{ + rzg2l_cru_write(cru, CRUnIE2, CRUnIE2_FSxE(cru->svc_channel)); + rzg2l_cru_write(cru, CRUnIE2, CRUnIE2_FExE(cru->svc_channel)); +} + +void rzg3e_cru_disable_interrupts(struct rzg2l_cru_dev *cru) +{ + rzg2l_cru_write(cru, CRUnIE, 0); + rzg2l_cru_write(cru, CRUnIE2, 0); + rzg2l_cru_write(cru, CRUnINTS, rzg2l_cru_read(cru, CRUnINTS)); + rzg2l_cru_write(cru, CRUnINTS2, rzg2l_cru_read(cru, CRUnINTS2)); +} + +void rzg2l_cru_enable_interrupts(struct rzg2l_cru_dev *cru) +{ + rzg2l_cru_write(cru, CRUnIE, CRUnIE_EFE); +} + +void rzg2l_cru_disable_interrupts(struct rzg2l_cru_dev *cru) +{ + rzg2l_cru_write(cru, CRUnIE, 0); + rzg2l_cru_write(cru, CRUnINTS, 0x001f000f); +} + int rzg2l_cru_start_image_processing(struct rzg2l_cru_dev *cru) { struct v4l2_mbus_framefmt *fmt = rzg2l_cru_ip_get_src_fmt(cru); @@ -352,6 +483,7 @@ int rzg2l_cru_start_image_processing(struct rzg2l_cru_dev *cru) if (ret < 0) return ret; csi_vc = ret; + cru->svc_channel = csi_vc; spin_lock_irqsave(&cru->qlock, flags); @@ -362,8 +494,7 @@ int rzg2l_cru_start_image_processing(struct rzg2l_cru_dev *cru) rzg2l_cru_write(cru, CRUnRST, CRUnRST_VRESETN); /* Disable and clear the interrupt before using */ - rzg2l_cru_write(cru, CRUnIE, 0); - rzg2l_cru_write(cru, CRUnINTS, 0x001f000f); + cru->info->disable_interrupts(cru); /* Initialize the AXI master */ rzg2l_cru_initialize_axi(cru); @@ -376,7 +507,7 @@ int rzg2l_cru_start_image_processing(struct rzg2l_cru_dev *cru) } /* Enable interrupt */ - rzg2l_cru_write(cru, CRUnIE, CRUnIE_EFE); + cru->info->enable_interrupts(cru); /* Enable image processing reception */ rzg2l_cru_write(cru, ICnEN, ICnEN_ICEN); @@ -531,6 +662,104 @@ done: return IRQ_RETVAL(handled); } +static int rzg3e_cru_get_current_slot(struct rzg2l_cru_dev *cru) +{ + u64 amnmadrs; + int slot; + + /* + * When AMnMADRSL is read, AMnMADRSH of the higher-order + * address also latches the address. + * + * AMnMADRSH must be read after AMnMADRSL has been read. + */ + amnmadrs = rzg2l_cru_read(cru, AMnMADRSL); + amnmadrs |= (u64)rzg2l_cru_read(cru, AMnMADRSH) << 32; + + /* Ensure amnmadrs is within this buffer range */ + for (slot = 0; slot < cru->num_buf; slot++) { + if (amnmadrs >= cru->buf_addr[slot] && + amnmadrs < cru->buf_addr[slot] + cru->format.sizeimage) + return slot; + } + + dev_err(cru->dev, "Invalid MB address 0x%llx (out of range)\n", amnmadrs); + return -EINVAL; +} + +irqreturn_t rzg3e_cru_irq(int irq, void *data) +{ + struct rzg2l_cru_dev *cru = data; + u32 irq_status; + int slot; + + scoped_guard(spinlock, &cru->qlock) { + irq_status = rzg2l_cru_read(cru, CRUnINTS2); + if (!irq_status) + return IRQ_NONE; + + dev_dbg(cru->dev, "CRUnINTS2 0x%x\n", irq_status); + + rzg2l_cru_write(cru, CRUnINTS2, rzg2l_cru_read(cru, CRUnINTS2)); + + /* Nothing to do if capture status is 'RZG2L_CRU_DMA_STOPPED' */ + if (cru->state == RZG2L_CRU_DMA_STOPPED) { + dev_dbg(cru->dev, "IRQ while state stopped\n"); + return IRQ_HANDLED; + } + + if (cru->state == RZG2L_CRU_DMA_STOPPING) { + if (irq_status & CRUnINTS2_FSxS(0) || + irq_status & CRUnINTS2_FSxS(1) || + irq_status & CRUnINTS2_FSxS(2) || + irq_status & CRUnINTS2_FSxS(3)) + dev_dbg(cru->dev, "IRQ while state stopping\n"); + return IRQ_HANDLED; + } + + slot = rzg3e_cru_get_current_slot(cru); + if (slot < 0) + return IRQ_HANDLED; + + dev_dbg(cru->dev, "Current written slot: %d\n", slot); + cru->buf_addr[slot] = 0; + + /* + * To hand buffers back in a known order to userspace start + * to capture first from slot 0. + */ + if (cru->state == RZG2L_CRU_DMA_STARTING) { + if (slot != 0) { + dev_dbg(cru->dev, "Starting sync slot: %d\n", slot); + return IRQ_HANDLED; + } + dev_dbg(cru->dev, "Capture start synced!\n"); + cru->state = RZG2L_CRU_DMA_RUNNING; + } + + /* Capture frame */ + if (cru->queue_buf[slot]) { + struct vb2_v4l2_buffer *buf = cru->queue_buf[slot]; + + buf->field = cru->format.field; + buf->sequence = cru->sequence; + buf->vb2_buf.timestamp = ktime_get_ns(); + vb2_buffer_done(&buf->vb2_buf, VB2_BUF_STATE_DONE); + cru->queue_buf[slot] = NULL; + } else { + /* Scratch buffer was used, dropping frame. */ + dev_dbg(cru->dev, "Dropping frame %u\n", cru->sequence); + } + + cru->sequence++; + + /* Prepare for next frame */ + rzg2l_cru_fill_hw_slot(cru, slot); + } + + return IRQ_HANDLED; +} + static int rzg2l_cru_start_streaming_vq(struct vb2_queue *vq, unsigned int count) { struct rzg2l_cru_dev *cru = vb2_get_drv_priv(vq); @@ -686,6 +915,7 @@ error: static void rzg2l_cru_format_align(struct rzg2l_cru_dev *cru, struct v4l2_pix_format *pix) { + const struct rzg2l_cru_info *info = cru->info; const struct rzg2l_cru_ip_format *fmt; fmt = rzg2l_cru_ip_format_to_fmt(pix->pixelformat); @@ -708,10 +938,17 @@ static void rzg2l_cru_format_align(struct rzg2l_cru_dev *cru, } /* Limit to CRU capabilities */ - v4l_bound_align_image(&pix->width, 320, RZG2L_CRU_MAX_INPUT_WIDTH, 1, - &pix->height, 240, RZG2L_CRU_MAX_INPUT_HEIGHT, 2, 0); + v4l_bound_align_image(&pix->width, 320, info->max_width, 1, + &pix->height, 240, info->max_height, 2, 0); + + if (info->has_stride) { + u32 stride = clamp(pix->bytesperline, pix->width * fmt->bpp, + RZG2L_CRU_STRIDE_MAX); + pix->bytesperline = round_up(stride, RZG2L_CRU_STRIDE_ALIGN); + } else { + pix->bytesperline = pix->width * fmt->bpp; + } - pix->bytesperline = pix->width * fmt->bpp; pix->sizeimage = pix->bytesperline * pix->height; dev_dbg(cru->dev, "Format %ux%u bpl: %u size: %u\n", |