linux/drivers/media/platform/coda/imx-vdoa.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * i.MX6 Video Data Order Adapter (VDOA)
   4 *
   5 * Copyright (C) 2014 Philipp Zabel
   6 * Copyright (C) 2016 Pengutronix, Michael Tretter <kernel@pengutronix.de>
   7 */
   8
   9#include <linux/clk.h>
  10#include <linux/device.h>
  11#include <linux/interrupt.h>
  12#include <linux/module.h>
  13#include <linux/mod_devicetable.h>
  14#include <linux/dma-mapping.h>
  15#include <linux/platform_device.h>
  16#include <linux/videodev2.h>
  17#include <linux/slab.h>
  18
  19#include "imx-vdoa.h"
  20
  21#define VDOA_NAME "imx-vdoa"
  22
  23#define VDOAC           0x00
  24#define VDOASRR         0x04
  25#define VDOAIE          0x08
  26#define VDOAIST         0x0c
  27#define VDOAFP          0x10
  28#define VDOAIEBA00      0x14
  29#define VDOAIEBA01      0x18
  30#define VDOAIEBA02      0x1c
  31#define VDOAIEBA10      0x20
  32#define VDOAIEBA11      0x24
  33#define VDOAIEBA12      0x28
  34#define VDOASL          0x2c
  35#define VDOAIUBO        0x30
  36#define VDOAVEBA0       0x34
  37#define VDOAVEBA1       0x38
  38#define VDOAVEBA2       0x3c
  39#define VDOAVUBO        0x40
  40#define VDOASR          0x44
  41
  42#define VDOAC_ISEL              BIT(6)
  43#define VDOAC_PFS               BIT(5)
  44#define VDOAC_SO                BIT(4)
  45#define VDOAC_SYNC              BIT(3)
  46#define VDOAC_NF                BIT(2)
  47#define VDOAC_BNDM_MASK         0x3
  48#define VDOAC_BAND_HEIGHT_8     0x0
  49#define VDOAC_BAND_HEIGHT_16    0x1
  50#define VDOAC_BAND_HEIGHT_32    0x2
  51
  52#define VDOASRR_START           BIT(1)
  53#define VDOASRR_SWRST           BIT(0)
  54
  55#define VDOAIE_EITERR           BIT(1)
  56#define VDOAIE_EIEOT            BIT(0)
  57
  58#define VDOAIST_TERR            BIT(1)
  59#define VDOAIST_EOT             BIT(0)
  60
  61#define VDOAFP_FH_MASK          (0x1fff << 16)
  62#define VDOAFP_FW_MASK          (0x3fff)
  63
  64#define VDOASL_VSLY_MASK        (0x3fff << 16)
  65#define VDOASL_ISLY_MASK        (0x7fff)
  66
  67#define VDOASR_ERRW             BIT(4)
  68#define VDOASR_EOB              BIT(3)
  69#define VDOASR_CURRENT_FRAME    (0x3 << 1)
  70#define VDOASR_CURRENT_BUFFER   BIT(1)
  71
  72enum {
  73        V4L2_M2M_SRC = 0,
  74        V4L2_M2M_DST = 1,
  75};
  76
  77struct vdoa_data {
  78        struct vdoa_ctx         *curr_ctx;
  79        struct device           *dev;
  80        struct clk              *vdoa_clk;
  81        void __iomem            *regs;
  82};
  83
  84struct vdoa_q_data {
  85        unsigned int    width;
  86        unsigned int    height;
  87        unsigned int    bytesperline;
  88        unsigned int    sizeimage;
  89        u32             pixelformat;
  90};
  91
  92struct vdoa_ctx {
  93        struct vdoa_data        *vdoa;
  94        struct completion       completion;
  95        struct vdoa_q_data      q_data[2];
  96        unsigned int            submitted_job;
  97        unsigned int            completed_job;
  98};
  99
 100static irqreturn_t vdoa_irq_handler(int irq, void *data)
 101{
 102        struct vdoa_data *vdoa = data;
 103        struct vdoa_ctx *curr_ctx;
 104        u32 val;
 105
 106        /* Disable interrupts */
 107        writel(0, vdoa->regs + VDOAIE);
 108
 109        curr_ctx = vdoa->curr_ctx;
 110        if (!curr_ctx) {
 111                dev_warn(vdoa->dev,
 112                        "Instance released before the end of transaction\n");
 113                return IRQ_HANDLED;
 114        }
 115
 116        val = readl(vdoa->regs + VDOAIST);
 117        writel(val, vdoa->regs + VDOAIST);
 118        if (val & VDOAIST_TERR) {
 119                val = readl(vdoa->regs + VDOASR) & VDOASR_ERRW;
 120                dev_err(vdoa->dev, "AXI %s error\n", val ? "write" : "read");
 121        } else if (!(val & VDOAIST_EOT)) {
 122                dev_warn(vdoa->dev, "Spurious interrupt\n");
 123        }
 124        curr_ctx->completed_job++;
 125        complete(&curr_ctx->completion);
 126
 127        return IRQ_HANDLED;
 128}
 129
 130int vdoa_wait_for_completion(struct vdoa_ctx *ctx)
 131{
 132        struct vdoa_data *vdoa = ctx->vdoa;
 133
 134        if (ctx->submitted_job == ctx->completed_job)
 135                return 0;
 136
 137        if (!wait_for_completion_timeout(&ctx->completion,
 138                                         msecs_to_jiffies(300))) {
 139                dev_err(vdoa->dev,
 140                        "Timeout waiting for transfer result\n");
 141                return -ETIMEDOUT;
 142        }
 143
 144        return 0;
 145}
 146EXPORT_SYMBOL(vdoa_wait_for_completion);
 147
 148void vdoa_device_run(struct vdoa_ctx *ctx, dma_addr_t dst, dma_addr_t src)
 149{
 150        struct vdoa_q_data *src_q_data, *dst_q_data;
 151        struct vdoa_data *vdoa = ctx->vdoa;
 152        u32 val;
 153
 154        if (vdoa->curr_ctx)
 155                vdoa_wait_for_completion(vdoa->curr_ctx);
 156
 157        vdoa->curr_ctx = ctx;
 158
 159        reinit_completion(&ctx->completion);
 160        ctx->submitted_job++;
 161
 162        src_q_data = &ctx->q_data[V4L2_M2M_SRC];
 163        dst_q_data = &ctx->q_data[V4L2_M2M_DST];
 164
 165        /* Progressive, no sync, 1 frame per run */
 166        if (dst_q_data->pixelformat == V4L2_PIX_FMT_YUYV)
 167                val = VDOAC_PFS;
 168        else
 169                val = 0;
 170        writel(val, vdoa->regs + VDOAC);
 171
 172        writel(dst_q_data->height << 16 | dst_q_data->width,
 173               vdoa->regs + VDOAFP);
 174
 175        val = dst;
 176        writel(val, vdoa->regs + VDOAIEBA00);
 177
 178        writel(src_q_data->bytesperline << 16 | dst_q_data->bytesperline,
 179               vdoa->regs + VDOASL);
 180
 181        if (dst_q_data->pixelformat == V4L2_PIX_FMT_NV12 ||
 182            dst_q_data->pixelformat == V4L2_PIX_FMT_NV21)
 183                val = dst_q_data->bytesperline * dst_q_data->height;
 184        else
 185                val = 0;
 186        writel(val, vdoa->regs + VDOAIUBO);
 187
 188        val = src;
 189        writel(val, vdoa->regs + VDOAVEBA0);
 190        val = round_up(src_q_data->bytesperline * src_q_data->height, 4096);
 191        writel(val, vdoa->regs + VDOAVUBO);
 192
 193        /* Enable interrupts and start transfer */
 194        writel(VDOAIE_EITERR | VDOAIE_EIEOT, vdoa->regs + VDOAIE);
 195        writel(VDOASRR_START, vdoa->regs + VDOASRR);
 196}
 197EXPORT_SYMBOL(vdoa_device_run);
 198
 199struct vdoa_ctx *vdoa_context_create(struct vdoa_data *vdoa)
 200{
 201        struct vdoa_ctx *ctx;
 202        int err;
 203
 204        ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
 205        if (!ctx)
 206                return NULL;
 207
 208        err = clk_prepare_enable(vdoa->vdoa_clk);
 209        if (err) {
 210                kfree(ctx);
 211                return NULL;
 212        }
 213
 214        init_completion(&ctx->completion);
 215        ctx->vdoa = vdoa;
 216
 217        return ctx;
 218}
 219EXPORT_SYMBOL(vdoa_context_create);
 220
 221void vdoa_context_destroy(struct vdoa_ctx *ctx)
 222{
 223        struct vdoa_data *vdoa = ctx->vdoa;
 224
 225        if (vdoa->curr_ctx == ctx) {
 226                vdoa_wait_for_completion(vdoa->curr_ctx);
 227                vdoa->curr_ctx = NULL;
 228        }
 229
 230        clk_disable_unprepare(vdoa->vdoa_clk);
 231        kfree(ctx);
 232}
 233EXPORT_SYMBOL(vdoa_context_destroy);
 234
 235int vdoa_context_configure(struct vdoa_ctx *ctx,
 236                           unsigned int width, unsigned int height,
 237                           u32 pixelformat)
 238{
 239        struct vdoa_q_data *src_q_data;
 240        struct vdoa_q_data *dst_q_data;
 241
 242        if (width < 16 || width  > 8192 || width % 16 != 0 ||
 243            height < 16 || height > 4096 || height % 16 != 0)
 244                return -EINVAL;
 245
 246        if (pixelformat != V4L2_PIX_FMT_YUYV &&
 247            pixelformat != V4L2_PIX_FMT_NV12)
 248                return -EINVAL;
 249
 250        /* If no context is passed, only check if the format is valid */
 251        if (!ctx)
 252                return 0;
 253
 254        src_q_data = &ctx->q_data[V4L2_M2M_SRC];
 255        dst_q_data = &ctx->q_data[V4L2_M2M_DST];
 256
 257        src_q_data->width = width;
 258        src_q_data->height = height;
 259        src_q_data->bytesperline = width;
 260        src_q_data->sizeimage =
 261                round_up(src_q_data->bytesperline * height, 4096) +
 262                src_q_data->bytesperline * height / 2;
 263
 264        dst_q_data->width = width;
 265        dst_q_data->height = height;
 266        dst_q_data->pixelformat = pixelformat;
 267        switch (pixelformat) {
 268        case V4L2_PIX_FMT_YUYV:
 269                dst_q_data->bytesperline = width * 2;
 270                dst_q_data->sizeimage = dst_q_data->bytesperline * height;
 271                break;
 272        case V4L2_PIX_FMT_NV12:
 273        default:
 274                dst_q_data->bytesperline = width;
 275                dst_q_data->sizeimage =
 276                        dst_q_data->bytesperline * height * 3 / 2;
 277                break;
 278        }
 279
 280        return 0;
 281}
 282EXPORT_SYMBOL(vdoa_context_configure);
 283
 284static int vdoa_probe(struct platform_device *pdev)
 285{
 286        struct vdoa_data *vdoa;
 287        struct resource *res;
 288        int ret;
 289
 290        dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32));
 291
 292        vdoa = devm_kzalloc(&pdev->dev, sizeof(*vdoa), GFP_KERNEL);
 293        if (!vdoa)
 294                return -ENOMEM;
 295
 296        vdoa->dev = &pdev->dev;
 297
 298        vdoa->vdoa_clk = devm_clk_get(vdoa->dev, NULL);
 299        if (IS_ERR(vdoa->vdoa_clk)) {
 300                dev_err(vdoa->dev, "Failed to get clock\n");
 301                return PTR_ERR(vdoa->vdoa_clk);
 302        }
 303
 304        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 305        vdoa->regs = devm_ioremap_resource(vdoa->dev, res);
 306        if (IS_ERR(vdoa->regs))
 307                return PTR_ERR(vdoa->regs);
 308
 309        res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
 310        if (!res)
 311                return -EINVAL;
 312        ret = devm_request_threaded_irq(&pdev->dev, res->start, NULL,
 313                                        vdoa_irq_handler, IRQF_ONESHOT,
 314                                        "vdoa", vdoa);
 315        if (ret < 0) {
 316                dev_err(vdoa->dev, "Failed to get irq\n");
 317                return ret;
 318        }
 319
 320        platform_set_drvdata(pdev, vdoa);
 321
 322        return 0;
 323}
 324
 325static int vdoa_remove(struct platform_device *pdev)
 326{
 327        return 0;
 328}
 329
 330static const struct of_device_id vdoa_dt_ids[] = {
 331        { .compatible = "fsl,imx6q-vdoa" },
 332        {}
 333};
 334MODULE_DEVICE_TABLE(of, vdoa_dt_ids);
 335
 336static struct platform_driver vdoa_driver = {
 337        .probe          = vdoa_probe,
 338        .remove         = vdoa_remove,
 339        .driver         = {
 340                .name   = VDOA_NAME,
 341                .of_match_table = vdoa_dt_ids,
 342        },
 343};
 344
 345module_platform_driver(vdoa_driver);
 346
 347MODULE_DESCRIPTION("Video Data Order Adapter");
 348MODULE_AUTHOR("Philipp Zabel <philipp.zabel@gmail.com>");
 349MODULE_ALIAS("platform:imx-vdoa");
 350MODULE_LICENSE("GPL");
 351