1*d2a34232SLucas Stach /* 2*d2a34232SLucas Stach * Copyright (c) 2017 Lucas Stach, Pengutronix 3*d2a34232SLucas Stach * 4*d2a34232SLucas Stach * This program is free software; you can redistribute it and/or modify it 5*d2a34232SLucas Stach * under the terms and conditions of the GNU General Public License, 6*d2a34232SLucas Stach * version 2, as published by the Free Software Foundation. 7*d2a34232SLucas Stach * 8*d2a34232SLucas Stach * This program is distributed in the hope it will be useful, but WITHOUT 9*d2a34232SLucas Stach * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 10*d2a34232SLucas Stach * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 11*d2a34232SLucas Stach * more details. 12*d2a34232SLucas Stach */ 13*d2a34232SLucas Stach 14*d2a34232SLucas Stach #include <drm/drm_fourcc.h> 15*d2a34232SLucas Stach #include <linux/clk.h> 16*d2a34232SLucas Stach #include <linux/err.h> 17*d2a34232SLucas Stach #include <linux/genalloc.h> 18*d2a34232SLucas Stach #include <linux/module.h> 19*d2a34232SLucas Stach #include <linux/of.h> 20*d2a34232SLucas Stach #include <linux/platform_device.h> 21*d2a34232SLucas Stach #include <video/imx-ipu-v3.h> 22*d2a34232SLucas Stach 23*d2a34232SLucas Stach #include "ipu-prv.h" 24*d2a34232SLucas Stach 25*d2a34232SLucas Stach #define IPU_PRE_MAX_WIDTH 2048 26*d2a34232SLucas Stach #define IPU_PRE_NUM_SCANLINES 8 27*d2a34232SLucas Stach 28*d2a34232SLucas Stach #define IPU_PRE_CTRL 0x000 29*d2a34232SLucas Stach #define IPU_PRE_CTRL_SET 0x004 30*d2a34232SLucas Stach #define IPU_PRE_CTRL_ENABLE (1 << 0) 31*d2a34232SLucas Stach #define IPU_PRE_CTRL_BLOCK_EN (1 << 1) 32*d2a34232SLucas Stach #define IPU_PRE_CTRL_BLOCK_16 (1 << 2) 33*d2a34232SLucas Stach #define IPU_PRE_CTRL_SDW_UPDATE (1 << 4) 34*d2a34232SLucas Stach #define IPU_PRE_CTRL_VFLIP (1 << 5) 35*d2a34232SLucas Stach #define IPU_PRE_CTRL_SO (1 << 6) 36*d2a34232SLucas Stach #define IPU_PRE_CTRL_INTERLACED_FIELD (1 << 7) 37*d2a34232SLucas Stach #define IPU_PRE_CTRL_HANDSHAKE_EN (1 << 8) 38*d2a34232SLucas Stach #define IPU_PRE_CTRL_HANDSHAKE_LINE_NUM(v) ((v & 0x3) << 9) 39*d2a34232SLucas Stach #define IPU_PRE_CTRL_HANDSHAKE_ABORT_SKIP_EN (1 << 11) 40*d2a34232SLucas Stach #define IPU_PRE_CTRL_EN_REPEAT (1 << 28) 41*d2a34232SLucas Stach #define IPU_PRE_CTRL_TPR_REST_SEL (1 << 29) 42*d2a34232SLucas Stach #define IPU_PRE_CTRL_CLKGATE (1 << 30) 43*d2a34232SLucas Stach #define IPU_PRE_CTRL_SFTRST (1 << 31) 44*d2a34232SLucas Stach 45*d2a34232SLucas Stach #define IPU_PRE_CUR_BUF 0x030 46*d2a34232SLucas Stach 47*d2a34232SLucas Stach #define IPU_PRE_NEXT_BUF 0x040 48*d2a34232SLucas Stach 49*d2a34232SLucas Stach #define IPU_PRE_TPR_CTRL 0x070 50*d2a34232SLucas Stach #define IPU_PRE_TPR_CTRL_TILE_FORMAT(v) ((v & 0xff) << 0) 51*d2a34232SLucas Stach #define IPU_PRE_TPR_CTRL_TILE_FORMAT_MASK 0xff 52*d2a34232SLucas Stach 53*d2a34232SLucas Stach #define IPU_PRE_PREFETCH_ENG_CTRL 0x080 54*d2a34232SLucas Stach #define IPU_PRE_PREF_ENG_CTRL_PREFETCH_EN (1 << 0) 55*d2a34232SLucas Stach #define IPU_PRE_PREF_ENG_CTRL_RD_NUM_BYTES(v) ((v & 0x7) << 1) 56*d2a34232SLucas Stach #define IPU_PRE_PREF_ENG_CTRL_INPUT_ACTIVE_BPP(v) ((v & 0x3) << 4) 57*d2a34232SLucas Stach #define IPU_PRE_PREF_ENG_CTRL_INPUT_PIXEL_FORMAT(v) ((v & 0x7) << 8) 58*d2a34232SLucas Stach #define IPU_PRE_PREF_ENG_CTRL_SHIFT_BYPASS (1 << 11) 59*d2a34232SLucas Stach #define IPU_PRE_PREF_ENG_CTRL_FIELD_INVERSE (1 << 12) 60*d2a34232SLucas Stach #define IPU_PRE_PREF_ENG_CTRL_PARTIAL_UV_SWAP (1 << 14) 61*d2a34232SLucas Stach #define IPU_PRE_PREF_ENG_CTRL_TPR_COOR_OFFSET_EN (1 << 15) 62*d2a34232SLucas Stach 63*d2a34232SLucas Stach #define IPU_PRE_PREFETCH_ENG_INPUT_SIZE 0x0a0 64*d2a34232SLucas Stach #define IPU_PRE_PREFETCH_ENG_INPUT_SIZE_WIDTH(v) ((v & 0xffff) << 0) 65*d2a34232SLucas Stach #define IPU_PRE_PREFETCH_ENG_INPUT_SIZE_HEIGHT(v) ((v & 0xffff) << 16) 66*d2a34232SLucas Stach 67*d2a34232SLucas Stach #define IPU_PRE_PREFETCH_ENG_PITCH 0x0d0 68*d2a34232SLucas Stach #define IPU_PRE_PREFETCH_ENG_PITCH_Y(v) ((v & 0xffff) << 0) 69*d2a34232SLucas Stach #define IPU_PRE_PREFETCH_ENG_PITCH_UV(v) ((v & 0xffff) << 16) 70*d2a34232SLucas Stach 71*d2a34232SLucas Stach #define IPU_PRE_STORE_ENG_CTRL 0x110 72*d2a34232SLucas Stach #define IPU_PRE_STORE_ENG_CTRL_STORE_EN (1 << 0) 73*d2a34232SLucas Stach #define IPU_PRE_STORE_ENG_CTRL_WR_NUM_BYTES(v) ((v & 0x7) << 1) 74*d2a34232SLucas Stach #define IPU_PRE_STORE_ENG_CTRL_OUTPUT_ACTIVE_BPP(v) ((v & 0x3) << 4) 75*d2a34232SLucas Stach 76*d2a34232SLucas Stach #define IPU_PRE_STORE_ENG_SIZE 0x130 77*d2a34232SLucas Stach #define IPU_PRE_STORE_ENG_SIZE_INPUT_WIDTH(v) ((v & 0xffff) << 0) 78*d2a34232SLucas Stach #define IPU_PRE_STORE_ENG_SIZE_INPUT_HEIGHT(v) ((v & 0xffff) << 16) 79*d2a34232SLucas Stach 80*d2a34232SLucas Stach #define IPU_PRE_STORE_ENG_PITCH 0x140 81*d2a34232SLucas Stach #define IPU_PRE_STORE_ENG_PITCH_OUT_PITCH(v) ((v & 0xffff) << 0) 82*d2a34232SLucas Stach 83*d2a34232SLucas Stach #define IPU_PRE_STORE_ENG_ADDR 0x150 84*d2a34232SLucas Stach 85*d2a34232SLucas Stach struct ipu_pre { 86*d2a34232SLucas Stach struct list_head list; 87*d2a34232SLucas Stach struct device *dev; 88*d2a34232SLucas Stach 89*d2a34232SLucas Stach void __iomem *regs; 90*d2a34232SLucas Stach struct clk *clk_axi; 91*d2a34232SLucas Stach struct gen_pool *iram; 92*d2a34232SLucas Stach 93*d2a34232SLucas Stach dma_addr_t buffer_paddr; 94*d2a34232SLucas Stach void *buffer_virt; 95*d2a34232SLucas Stach bool in_use; 96*d2a34232SLucas Stach }; 97*d2a34232SLucas Stach 98*d2a34232SLucas Stach static DEFINE_MUTEX(ipu_pre_list_mutex); 99*d2a34232SLucas Stach static LIST_HEAD(ipu_pre_list); 100*d2a34232SLucas Stach static int available_pres; 101*d2a34232SLucas Stach 102*d2a34232SLucas Stach int ipu_pre_get_available_count(void) 103*d2a34232SLucas Stach { 104*d2a34232SLucas Stach return available_pres; 105*d2a34232SLucas Stach } 106*d2a34232SLucas Stach 107*d2a34232SLucas Stach struct ipu_pre * 108*d2a34232SLucas Stach ipu_pre_lookup_by_phandle(struct device *dev, const char *name, int index) 109*d2a34232SLucas Stach { 110*d2a34232SLucas Stach struct device_node *pre_node = of_parse_phandle(dev->of_node, 111*d2a34232SLucas Stach name, index); 112*d2a34232SLucas Stach struct ipu_pre *pre; 113*d2a34232SLucas Stach 114*d2a34232SLucas Stach mutex_lock(&ipu_pre_list_mutex); 115*d2a34232SLucas Stach list_for_each_entry(pre, &ipu_pre_list, list) { 116*d2a34232SLucas Stach if (pre_node == pre->dev->of_node) { 117*d2a34232SLucas Stach mutex_unlock(&ipu_pre_list_mutex); 118*d2a34232SLucas Stach device_link_add(dev, pre->dev, DL_FLAG_AUTOREMOVE); 119*d2a34232SLucas Stach return pre; 120*d2a34232SLucas Stach } 121*d2a34232SLucas Stach } 122*d2a34232SLucas Stach mutex_unlock(&ipu_pre_list_mutex); 123*d2a34232SLucas Stach 124*d2a34232SLucas Stach return NULL; 125*d2a34232SLucas Stach } 126*d2a34232SLucas Stach 127*d2a34232SLucas Stach int ipu_pre_get(struct ipu_pre *pre) 128*d2a34232SLucas Stach { 129*d2a34232SLucas Stach u32 val; 130*d2a34232SLucas Stach 131*d2a34232SLucas Stach if (pre->in_use) 132*d2a34232SLucas Stach return -EBUSY; 133*d2a34232SLucas Stach 134*d2a34232SLucas Stach clk_prepare_enable(pre->clk_axi); 135*d2a34232SLucas Stach 136*d2a34232SLucas Stach /* first get the engine out of reset and remove clock gating */ 137*d2a34232SLucas Stach writel(0, pre->regs + IPU_PRE_CTRL); 138*d2a34232SLucas Stach 139*d2a34232SLucas Stach /* init defaults that should be applied to all streams */ 140*d2a34232SLucas Stach val = IPU_PRE_CTRL_HANDSHAKE_ABORT_SKIP_EN | 141*d2a34232SLucas Stach IPU_PRE_CTRL_HANDSHAKE_EN | 142*d2a34232SLucas Stach IPU_PRE_CTRL_TPR_REST_SEL | 143*d2a34232SLucas Stach IPU_PRE_CTRL_BLOCK_16 | IPU_PRE_CTRL_SDW_UPDATE; 144*d2a34232SLucas Stach writel(val, pre->regs + IPU_PRE_CTRL); 145*d2a34232SLucas Stach 146*d2a34232SLucas Stach pre->in_use = true; 147*d2a34232SLucas Stach return 0; 148*d2a34232SLucas Stach } 149*d2a34232SLucas Stach 150*d2a34232SLucas Stach void ipu_pre_put(struct ipu_pre *pre) 151*d2a34232SLucas Stach { 152*d2a34232SLucas Stach u32 val; 153*d2a34232SLucas Stach 154*d2a34232SLucas Stach val = IPU_PRE_CTRL_SFTRST | IPU_PRE_CTRL_CLKGATE; 155*d2a34232SLucas Stach writel(val, pre->regs + IPU_PRE_CTRL); 156*d2a34232SLucas Stach 157*d2a34232SLucas Stach clk_disable_unprepare(pre->clk_axi); 158*d2a34232SLucas Stach 159*d2a34232SLucas Stach pre->in_use = false; 160*d2a34232SLucas Stach } 161*d2a34232SLucas Stach 162*d2a34232SLucas Stach void ipu_pre_configure(struct ipu_pre *pre, unsigned int width, 163*d2a34232SLucas Stach unsigned int height, unsigned int stride, u32 format, 164*d2a34232SLucas Stach unsigned int bufaddr) 165*d2a34232SLucas Stach { 166*d2a34232SLucas Stach const struct drm_format_info *info = drm_format_info(format); 167*d2a34232SLucas Stach u32 active_bpp = info->cpp[0] >> 1; 168*d2a34232SLucas Stach u32 val; 169*d2a34232SLucas Stach 170*d2a34232SLucas Stach writel(bufaddr, pre->regs + IPU_PRE_CUR_BUF); 171*d2a34232SLucas Stach writel(bufaddr, pre->regs + IPU_PRE_NEXT_BUF); 172*d2a34232SLucas Stach 173*d2a34232SLucas Stach val = IPU_PRE_PREF_ENG_CTRL_INPUT_PIXEL_FORMAT(0) | 174*d2a34232SLucas Stach IPU_PRE_PREF_ENG_CTRL_INPUT_ACTIVE_BPP(active_bpp) | 175*d2a34232SLucas Stach IPU_PRE_PREF_ENG_CTRL_RD_NUM_BYTES(4) | 176*d2a34232SLucas Stach IPU_PRE_PREF_ENG_CTRL_SHIFT_BYPASS | 177*d2a34232SLucas Stach IPU_PRE_PREF_ENG_CTRL_PREFETCH_EN; 178*d2a34232SLucas Stach writel(val, pre->regs + IPU_PRE_PREFETCH_ENG_CTRL); 179*d2a34232SLucas Stach 180*d2a34232SLucas Stach val = IPU_PRE_PREFETCH_ENG_INPUT_SIZE_WIDTH(width) | 181*d2a34232SLucas Stach IPU_PRE_PREFETCH_ENG_INPUT_SIZE_HEIGHT(height); 182*d2a34232SLucas Stach writel(val, pre->regs + IPU_PRE_PREFETCH_ENG_INPUT_SIZE); 183*d2a34232SLucas Stach 184*d2a34232SLucas Stach val = IPU_PRE_PREFETCH_ENG_PITCH_Y(stride); 185*d2a34232SLucas Stach writel(val, pre->regs + IPU_PRE_PREFETCH_ENG_PITCH); 186*d2a34232SLucas Stach 187*d2a34232SLucas Stach val = IPU_PRE_STORE_ENG_CTRL_OUTPUT_ACTIVE_BPP(active_bpp) | 188*d2a34232SLucas Stach IPU_PRE_STORE_ENG_CTRL_WR_NUM_BYTES(4) | 189*d2a34232SLucas Stach IPU_PRE_STORE_ENG_CTRL_STORE_EN; 190*d2a34232SLucas Stach writel(val, pre->regs + IPU_PRE_STORE_ENG_CTRL); 191*d2a34232SLucas Stach 192*d2a34232SLucas Stach val = IPU_PRE_STORE_ENG_SIZE_INPUT_WIDTH(width) | 193*d2a34232SLucas Stach IPU_PRE_STORE_ENG_SIZE_INPUT_HEIGHT(height); 194*d2a34232SLucas Stach writel(val, pre->regs + IPU_PRE_STORE_ENG_SIZE); 195*d2a34232SLucas Stach 196*d2a34232SLucas Stach val = IPU_PRE_STORE_ENG_PITCH_OUT_PITCH(stride); 197*d2a34232SLucas Stach writel(val, pre->regs + IPU_PRE_STORE_ENG_PITCH); 198*d2a34232SLucas Stach 199*d2a34232SLucas Stach writel(pre->buffer_paddr, pre->regs + IPU_PRE_STORE_ENG_ADDR); 200*d2a34232SLucas Stach 201*d2a34232SLucas Stach val = readl(pre->regs + IPU_PRE_CTRL); 202*d2a34232SLucas Stach val |= IPU_PRE_CTRL_EN_REPEAT | IPU_PRE_CTRL_ENABLE | 203*d2a34232SLucas Stach IPU_PRE_CTRL_SDW_UPDATE; 204*d2a34232SLucas Stach writel(val, pre->regs + IPU_PRE_CTRL); 205*d2a34232SLucas Stach } 206*d2a34232SLucas Stach 207*d2a34232SLucas Stach void ipu_pre_update(struct ipu_pre *pre, unsigned int bufaddr) 208*d2a34232SLucas Stach { 209*d2a34232SLucas Stach writel(bufaddr, pre->regs + IPU_PRE_NEXT_BUF); 210*d2a34232SLucas Stach writel(IPU_PRE_CTRL_SDW_UPDATE, pre->regs + IPU_PRE_CTRL_SET); 211*d2a34232SLucas Stach } 212*d2a34232SLucas Stach 213*d2a34232SLucas Stach u32 ipu_pre_get_baddr(struct ipu_pre *pre) 214*d2a34232SLucas Stach { 215*d2a34232SLucas Stach return (u32)pre->buffer_paddr; 216*d2a34232SLucas Stach } 217*d2a34232SLucas Stach 218*d2a34232SLucas Stach static int ipu_pre_probe(struct platform_device *pdev) 219*d2a34232SLucas Stach { 220*d2a34232SLucas Stach struct device *dev = &pdev->dev; 221*d2a34232SLucas Stach struct resource *res; 222*d2a34232SLucas Stach struct ipu_pre *pre; 223*d2a34232SLucas Stach 224*d2a34232SLucas Stach pre = devm_kzalloc(dev, sizeof(*pre), GFP_KERNEL); 225*d2a34232SLucas Stach if (!pre) 226*d2a34232SLucas Stach return -ENOMEM; 227*d2a34232SLucas Stach 228*d2a34232SLucas Stach res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 229*d2a34232SLucas Stach pre->regs = devm_ioremap_resource(&pdev->dev, res); 230*d2a34232SLucas Stach if (IS_ERR(pre->regs)) 231*d2a34232SLucas Stach return PTR_ERR(pre->regs); 232*d2a34232SLucas Stach 233*d2a34232SLucas Stach pre->clk_axi = devm_clk_get(dev, "axi"); 234*d2a34232SLucas Stach if (IS_ERR(pre->clk_axi)) 235*d2a34232SLucas Stach return PTR_ERR(pre->clk_axi); 236*d2a34232SLucas Stach 237*d2a34232SLucas Stach pre->iram = of_gen_pool_get(dev->of_node, "fsl,iram", 0); 238*d2a34232SLucas Stach if (!pre->iram) 239*d2a34232SLucas Stach return -EPROBE_DEFER; 240*d2a34232SLucas Stach 241*d2a34232SLucas Stach /* 242*d2a34232SLucas Stach * Allocate IRAM buffer with maximum size. This could be made dynamic, 243*d2a34232SLucas Stach * but as there is no other user of this IRAM region and we can fit all 244*d2a34232SLucas Stach * max sized buffers into it, there is no need yet. 245*d2a34232SLucas Stach */ 246*d2a34232SLucas Stach pre->buffer_virt = gen_pool_dma_alloc(pre->iram, IPU_PRE_MAX_WIDTH * 247*d2a34232SLucas Stach IPU_PRE_NUM_SCANLINES * 4, 248*d2a34232SLucas Stach &pre->buffer_paddr); 249*d2a34232SLucas Stach if (!pre->buffer_virt) 250*d2a34232SLucas Stach return -ENOMEM; 251*d2a34232SLucas Stach 252*d2a34232SLucas Stach pre->dev = dev; 253*d2a34232SLucas Stach platform_set_drvdata(pdev, pre); 254*d2a34232SLucas Stach mutex_lock(&ipu_pre_list_mutex); 255*d2a34232SLucas Stach list_add(&pre->list, &ipu_pre_list); 256*d2a34232SLucas Stach available_pres++; 257*d2a34232SLucas Stach mutex_unlock(&ipu_pre_list_mutex); 258*d2a34232SLucas Stach 259*d2a34232SLucas Stach return 0; 260*d2a34232SLucas Stach } 261*d2a34232SLucas Stach 262*d2a34232SLucas Stach static int ipu_pre_remove(struct platform_device *pdev) 263*d2a34232SLucas Stach { 264*d2a34232SLucas Stach struct ipu_pre *pre = platform_get_drvdata(pdev); 265*d2a34232SLucas Stach 266*d2a34232SLucas Stach mutex_lock(&ipu_pre_list_mutex); 267*d2a34232SLucas Stach list_del(&pre->list); 268*d2a34232SLucas Stach available_pres--; 269*d2a34232SLucas Stach mutex_unlock(&ipu_pre_list_mutex); 270*d2a34232SLucas Stach 271*d2a34232SLucas Stach if (pre->buffer_virt) 272*d2a34232SLucas Stach gen_pool_free(pre->iram, (unsigned long)pre->buffer_virt, 273*d2a34232SLucas Stach IPU_PRE_MAX_WIDTH * IPU_PRE_NUM_SCANLINES * 4); 274*d2a34232SLucas Stach return 0; 275*d2a34232SLucas Stach } 276*d2a34232SLucas Stach 277*d2a34232SLucas Stach static const struct of_device_id ipu_pre_dt_ids[] = { 278*d2a34232SLucas Stach { .compatible = "fsl,imx6qp-pre", }, 279*d2a34232SLucas Stach { /* sentinel */ }, 280*d2a34232SLucas Stach }; 281*d2a34232SLucas Stach 282*d2a34232SLucas Stach struct platform_driver ipu_pre_drv = { 283*d2a34232SLucas Stach .probe = ipu_pre_probe, 284*d2a34232SLucas Stach .remove = ipu_pre_remove, 285*d2a34232SLucas Stach .driver = { 286*d2a34232SLucas Stach .name = "imx-ipu-pre", 287*d2a34232SLucas Stach .of_match_table = ipu_pre_dt_ids, 288*d2a34232SLucas Stach }, 289*d2a34232SLucas Stach }; 290