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