383 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright (c) 2017 Lucas Stach, Pengutronix4 */5 6#include <drm/drm_fourcc.h>7#include <linux/clk.h>8#include <linux/delay.h>9#include <linux/err.h>10#include <linux/genalloc.h>11#include <linux/module.h>12#include <linux/of.h>13#include <linux/platform_device.h>14#include <video/imx-ipu-v3.h>15 16#include "ipu-prv.h"17 18#define IPU_PRE_MAX_WIDTH 204819#define IPU_PRE_NUM_SCANLINES 820 21#define IPU_PRE_CTRL 0x00022#define IPU_PRE_CTRL_SET 0x00423#define IPU_PRE_CTRL_ENABLE (1 << 0)24#define IPU_PRE_CTRL_BLOCK_EN (1 << 1)25#define IPU_PRE_CTRL_BLOCK_16 (1 << 2)26#define IPU_PRE_CTRL_SDW_UPDATE (1 << 4)27#define IPU_PRE_CTRL_VFLIP (1 << 5)28#define IPU_PRE_CTRL_SO (1 << 6)29#define IPU_PRE_CTRL_INTERLACED_FIELD (1 << 7)30#define IPU_PRE_CTRL_HANDSHAKE_EN (1 << 8)31#define IPU_PRE_CTRL_HANDSHAKE_LINE_NUM(v) ((v & 0x3) << 9)32#define IPU_PRE_CTRL_HANDSHAKE_ABORT_SKIP_EN (1 << 11)33#define IPU_PRE_CTRL_EN_REPEAT (1 << 28)34#define IPU_PRE_CTRL_TPR_REST_SEL (1 << 29)35#define IPU_PRE_CTRL_CLKGATE (1 << 30)36#define IPU_PRE_CTRL_SFTRST (1 << 31)37 38#define IPU_PRE_CUR_BUF 0x03039 40#define IPU_PRE_NEXT_BUF 0x04041 42#define IPU_PRE_TPR_CTRL 0x07043#define IPU_PRE_TPR_CTRL_TILE_FORMAT(v) ((v & 0xff) << 0)44#define IPU_PRE_TPR_CTRL_TILE_FORMAT_MASK 0xff45#define IPU_PRE_TPR_CTRL_TILE_FORMAT_16_BIT (1 << 0)46#define IPU_PRE_TPR_CTRL_TILE_FORMAT_SPLIT_BUF (1 << 4)47#define IPU_PRE_TPR_CTRL_TILE_FORMAT_SINGLE_BUF (1 << 5)48#define IPU_PRE_TPR_CTRL_TILE_FORMAT_SUPER_TILED (1 << 6)49 50#define IPU_PRE_PREFETCH_ENG_CTRL 0x08051#define IPU_PRE_PREF_ENG_CTRL_PREFETCH_EN (1 << 0)52#define IPU_PRE_PREF_ENG_CTRL_RD_NUM_BYTES(v) ((v & 0x7) << 1)53#define IPU_PRE_PREF_ENG_CTRL_INPUT_ACTIVE_BPP(v) ((v & 0x3) << 4)54#define IPU_PRE_PREF_ENG_CTRL_INPUT_PIXEL_FORMAT(v) ((v & 0x7) << 8)55#define IPU_PRE_PREF_ENG_CTRL_SHIFT_BYPASS (1 << 11)56#define IPU_PRE_PREF_ENG_CTRL_FIELD_INVERSE (1 << 12)57#define IPU_PRE_PREF_ENG_CTRL_PARTIAL_UV_SWAP (1 << 14)58#define IPU_PRE_PREF_ENG_CTRL_TPR_COOR_OFFSET_EN (1 << 15)59 60#define IPU_PRE_PREFETCH_ENG_INPUT_SIZE 0x0a061#define IPU_PRE_PREFETCH_ENG_INPUT_SIZE_WIDTH(v) ((v & 0xffff) << 0)62#define IPU_PRE_PREFETCH_ENG_INPUT_SIZE_HEIGHT(v) ((v & 0xffff) << 16)63 64#define IPU_PRE_PREFETCH_ENG_PITCH 0x0d065#define IPU_PRE_PREFETCH_ENG_PITCH_Y(v) ((v & 0xffff) << 0)66#define IPU_PRE_PREFETCH_ENG_PITCH_UV(v) ((v & 0xffff) << 16)67 68#define IPU_PRE_STORE_ENG_CTRL 0x11069#define IPU_PRE_STORE_ENG_CTRL_STORE_EN (1 << 0)70#define IPU_PRE_STORE_ENG_CTRL_WR_NUM_BYTES(v) ((v & 0x7) << 1)71#define IPU_PRE_STORE_ENG_CTRL_OUTPUT_ACTIVE_BPP(v) ((v & 0x3) << 4)72 73#define IPU_PRE_STORE_ENG_STATUS 0x12074#define IPU_PRE_STORE_ENG_STATUS_STORE_BLOCK_X_MASK 0xffff75#define IPU_PRE_STORE_ENG_STATUS_STORE_BLOCK_X_SHIFT 076#define IPU_PRE_STORE_ENG_STATUS_STORE_BLOCK_Y_MASK 0x3fff77#define IPU_PRE_STORE_ENG_STATUS_STORE_BLOCK_Y_SHIFT 1678#define IPU_PRE_STORE_ENG_STATUS_STORE_FIFO_FULL (1 << 30)79#define IPU_PRE_STORE_ENG_STATUS_STORE_FIELD (1 << 31)80 81#define IPU_PRE_STORE_ENG_SIZE 0x13082#define IPU_PRE_STORE_ENG_SIZE_INPUT_WIDTH(v) ((v & 0xffff) << 0)83#define IPU_PRE_STORE_ENG_SIZE_INPUT_HEIGHT(v) ((v & 0xffff) << 16)84 85#define IPU_PRE_STORE_ENG_PITCH 0x14086#define IPU_PRE_STORE_ENG_PITCH_OUT_PITCH(v) ((v & 0xffff) << 0)87 88#define IPU_PRE_STORE_ENG_ADDR 0x15089 90struct ipu_pre {91 struct list_head list;92 struct device *dev;93 94 void __iomem *regs;95 struct clk *clk_axi;96 struct gen_pool *iram;97 98 dma_addr_t buffer_paddr;99 void *buffer_virt;100 101 struct {102 bool in_use;103 uint64_t modifier;104 unsigned int height;105 unsigned int safe_window_end;106 unsigned int bufaddr;107 u32 ctrl;108 u8 cpp;109 } cur;110};111 112static DEFINE_MUTEX(ipu_pre_list_mutex);113static LIST_HEAD(ipu_pre_list);114static int available_pres;115 116int ipu_pre_get_available_count(void)117{118 return available_pres;119}120 121struct ipu_pre *122ipu_pre_lookup_by_phandle(struct device *dev, const char *name, int index)123{124 struct device_node *pre_node __free(device_node) =125 of_parse_phandle(dev->of_node, name, index);126 struct ipu_pre *pre;127 128 mutex_lock(&ipu_pre_list_mutex);129 list_for_each_entry(pre, &ipu_pre_list, list) {130 if (pre_node == pre->dev->of_node) {131 mutex_unlock(&ipu_pre_list_mutex);132 device_link_add(dev, pre->dev,133 DL_FLAG_AUTOREMOVE_CONSUMER);134 return pre;135 }136 }137 mutex_unlock(&ipu_pre_list_mutex);138 139 return NULL;140}141 142int ipu_pre_get(struct ipu_pre *pre)143{144 u32 val;145 146 if (pre->cur.in_use)147 return -EBUSY;148 149 /* first get the engine out of reset and remove clock gating */150 writel(0, pre->regs + IPU_PRE_CTRL);151 152 /* init defaults that should be applied to all streams */153 val = IPU_PRE_CTRL_HANDSHAKE_ABORT_SKIP_EN |154 IPU_PRE_CTRL_HANDSHAKE_EN |155 IPU_PRE_CTRL_TPR_REST_SEL |156 IPU_PRE_CTRL_SDW_UPDATE;157 writel(val, pre->regs + IPU_PRE_CTRL);158 159 pre->cur.in_use = true;160 return 0;161}162 163void ipu_pre_put(struct ipu_pre *pre)164{165 writel(IPU_PRE_CTRL_SFTRST, pre->regs + IPU_PRE_CTRL);166 167 pre->cur.in_use = false;168}169 170static inline void171ipu_pre_update_safe_window(struct ipu_pre *pre)172{173 if (pre->cur.modifier == DRM_FORMAT_MOD_LINEAR)174 pre->cur.safe_window_end = pre->cur.height - 2;175 else176 pre->cur.safe_window_end = DIV_ROUND_UP(pre->cur.height, 4) - 1;177}178 179static void180ipu_pre_configure_modifier(struct ipu_pre *pre, uint64_t modifier)181{182 u32 val;183 184 val = readl(pre->regs + IPU_PRE_TPR_CTRL);185 val &= ~IPU_PRE_TPR_CTRL_TILE_FORMAT_MASK;186 if (modifier != DRM_FORMAT_MOD_LINEAR) {187 /* only support single buffer formats for now */188 val |= IPU_PRE_TPR_CTRL_TILE_FORMAT_SINGLE_BUF;189 if (modifier == DRM_FORMAT_MOD_VIVANTE_SUPER_TILED)190 val |= IPU_PRE_TPR_CTRL_TILE_FORMAT_SUPER_TILED;191 if (pre->cur.cpp == 2)192 val |= IPU_PRE_TPR_CTRL_TILE_FORMAT_16_BIT;193 }194 writel(val, pre->regs + IPU_PRE_TPR_CTRL);195 196 if (modifier == DRM_FORMAT_MOD_LINEAR)197 pre->cur.ctrl &= ~IPU_PRE_CTRL_BLOCK_EN;198 else199 pre->cur.ctrl |= IPU_PRE_CTRL_BLOCK_EN;200 201 pre->cur.modifier = modifier;202}203 204void ipu_pre_configure(struct ipu_pre *pre, unsigned int width,205 unsigned int height, unsigned int stride, u32 format,206 uint64_t modifier, unsigned int bufaddr)207{208 const struct drm_format_info *info = drm_format_info(format);209 u32 active_bpp = info->cpp[0] >> 1;210 u32 val;211 212 pre->cur.bufaddr = bufaddr;213 pre->cur.height = height;214 pre->cur.cpp = info->cpp[0];215 pre->cur.ctrl = readl(pre->regs + IPU_PRE_CTRL);216 217 /* calculate safe window for ctrl register updates */218 ipu_pre_update_safe_window(pre);219 220 writel(bufaddr, pre->regs + IPU_PRE_CUR_BUF);221 writel(bufaddr, pre->regs + IPU_PRE_NEXT_BUF);222 223 val = IPU_PRE_PREF_ENG_CTRL_INPUT_PIXEL_FORMAT(0) |224 IPU_PRE_PREF_ENG_CTRL_INPUT_ACTIVE_BPP(active_bpp) |225 IPU_PRE_PREF_ENG_CTRL_RD_NUM_BYTES(4) |226 IPU_PRE_PREF_ENG_CTRL_SHIFT_BYPASS |227 IPU_PRE_PREF_ENG_CTRL_PREFETCH_EN;228 writel(val, pre->regs + IPU_PRE_PREFETCH_ENG_CTRL);229 230 val = IPU_PRE_PREFETCH_ENG_INPUT_SIZE_WIDTH(width) |231 IPU_PRE_PREFETCH_ENG_INPUT_SIZE_HEIGHT(height);232 writel(val, pre->regs + IPU_PRE_PREFETCH_ENG_INPUT_SIZE);233 234 val = IPU_PRE_PREFETCH_ENG_PITCH_Y(stride);235 writel(val, pre->regs + IPU_PRE_PREFETCH_ENG_PITCH);236 237 val = IPU_PRE_STORE_ENG_CTRL_OUTPUT_ACTIVE_BPP(active_bpp) |238 IPU_PRE_STORE_ENG_CTRL_WR_NUM_BYTES(4) |239 IPU_PRE_STORE_ENG_CTRL_STORE_EN;240 writel(val, pre->regs + IPU_PRE_STORE_ENG_CTRL);241 242 val = IPU_PRE_STORE_ENG_SIZE_INPUT_WIDTH(width) |243 IPU_PRE_STORE_ENG_SIZE_INPUT_HEIGHT(height);244 writel(val, pre->regs + IPU_PRE_STORE_ENG_SIZE);245 246 val = IPU_PRE_STORE_ENG_PITCH_OUT_PITCH(stride);247 writel(val, pre->regs + IPU_PRE_STORE_ENG_PITCH);248 249 writel(pre->buffer_paddr, pre->regs + IPU_PRE_STORE_ENG_ADDR);250 251 ipu_pre_configure_modifier(pre, modifier);252 253 pre->cur.ctrl |= IPU_PRE_CTRL_EN_REPEAT | IPU_PRE_CTRL_ENABLE;254 writel(pre->cur.ctrl | IPU_PRE_CTRL_SDW_UPDATE,255 pre->regs + IPU_PRE_CTRL);256}257 258void ipu_pre_update(struct ipu_pre *pre, uint64_t modifier, unsigned int bufaddr)259{260 if (bufaddr == pre->cur.bufaddr &&261 modifier == pre->cur.modifier)262 return;263 264 writel(bufaddr, pre->regs + IPU_PRE_NEXT_BUF);265 pre->cur.bufaddr = bufaddr;266 267 if (modifier != pre->cur.modifier)268 ipu_pre_configure_modifier(pre, modifier);269 270 for (int i = 0;; i++) {271 unsigned short current_yblock;272 u32 val;273 274 if (i > 500) {275 dev_warn(pre->dev, "timeout waiting for PRE safe window\n");276 return;277 }278 279 val = readl(pre->regs + IPU_PRE_STORE_ENG_STATUS);280 current_yblock =281 (val >> IPU_PRE_STORE_ENG_STATUS_STORE_BLOCK_Y_SHIFT) &282 IPU_PRE_STORE_ENG_STATUS_STORE_BLOCK_Y_MASK;283 284 if (current_yblock != 0 &&285 current_yblock < pre->cur.safe_window_end)286 break;287 288 udelay(10);289 cpu_relax();290 }291 292 writel(pre->cur.ctrl | IPU_PRE_CTRL_SDW_UPDATE,293 pre->regs + IPU_PRE_CTRL);294 295 /* calculate safe window for the next update with the new modifier */296 ipu_pre_update_safe_window(pre);297}298 299bool ipu_pre_update_pending(struct ipu_pre *pre)300{301 return !!(readl_relaxed(pre->regs + IPU_PRE_CTRL) &302 IPU_PRE_CTRL_SDW_UPDATE);303}304 305u32 ipu_pre_get_baddr(struct ipu_pre *pre)306{307 return (u32)pre->buffer_paddr;308}309 310static int ipu_pre_probe(struct platform_device *pdev)311{312 struct device *dev = &pdev->dev;313 struct ipu_pre *pre;314 315 pre = devm_kzalloc(dev, sizeof(*pre), GFP_KERNEL);316 if (!pre)317 return -ENOMEM;318 319 pre->regs = devm_platform_ioremap_resource(pdev, 0);320 if (IS_ERR(pre->regs))321 return PTR_ERR(pre->regs);322 323 pre->clk_axi = devm_clk_get(dev, "axi");324 if (IS_ERR(pre->clk_axi))325 return PTR_ERR(pre->clk_axi);326 327 pre->iram = of_gen_pool_get(dev->of_node, "fsl,iram", 0);328 if (!pre->iram)329 return -EPROBE_DEFER;330 331 /*332 * Allocate IRAM buffer with maximum size. This could be made dynamic,333 * but as there is no other user of this IRAM region and we can fit all334 * max sized buffers into it, there is no need yet.335 */336 pre->buffer_virt = gen_pool_dma_alloc(pre->iram, IPU_PRE_MAX_WIDTH *337 IPU_PRE_NUM_SCANLINES * 4,338 &pre->buffer_paddr);339 if (!pre->buffer_virt)340 return -ENOMEM;341 342 clk_prepare_enable(pre->clk_axi);343 344 pre->dev = dev;345 platform_set_drvdata(pdev, pre);346 mutex_lock(&ipu_pre_list_mutex);347 list_add(&pre->list, &ipu_pre_list);348 available_pres++;349 mutex_unlock(&ipu_pre_list_mutex);350 351 return 0;352}353 354static void ipu_pre_remove(struct platform_device *pdev)355{356 struct ipu_pre *pre = platform_get_drvdata(pdev);357 358 mutex_lock(&ipu_pre_list_mutex);359 list_del(&pre->list);360 available_pres--;361 mutex_unlock(&ipu_pre_list_mutex);362 363 clk_disable_unprepare(pre->clk_axi);364 365 if (pre->buffer_virt)366 gen_pool_free(pre->iram, (unsigned long)pre->buffer_virt,367 IPU_PRE_MAX_WIDTH * IPU_PRE_NUM_SCANLINES * 4);368}369 370static const struct of_device_id ipu_pre_dt_ids[] = {371 { .compatible = "fsl,imx6qp-pre", },372 { /* sentinel */ },373};374 375struct platform_driver ipu_pre_drv = {376 .probe = ipu_pre_probe,377 .remove_new = ipu_pre_remove,378 .driver = {379 .name = "imx-ipu-pre",380 .of_match_table = ipu_pre_dt_ids,381 },382};383