root/drivers/gpu/drm/exynos/exynos_drm_rotator.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. rotator_reg_set_irq
  2. rotator_reg_get_irq_status
  3. rotator_irq_handler
  4. rotator_src_set_fmt
  5. rotator_src_set_buf
  6. rotator_dst_set_transf
  7. rotator_dst_set_buf
  8. rotator_start
  9. rotator_commit
  10. rotator_bind
  11. rotator_unbind
  12. rotator_probe
  13. rotator_remove
  14. rotator_runtime_suspend
  15. rotator_runtime_resume

   1 // SPDX-License-Identifier: GPL-2.0-only
   2 /*
   3  * Copyright (C) 2012 Samsung Electronics Co.Ltd
   4  * Authors:
   5  *      YoungJun Cho <yj44.cho@samsung.com>
   6  *      Eunchul Kim <chulspro.kim@samsung.com>
   7  */
   8 
   9 #include <linux/clk.h>
  10 #include <linux/component.h>
  11 #include <linux/err.h>
  12 #include <linux/interrupt.h>
  13 #include <linux/io.h>
  14 #include <linux/kernel.h>
  15 #include <linux/of_device.h>
  16 #include <linux/platform_device.h>
  17 #include <linux/pm_runtime.h>
  18 #include <linux/sizes.h>
  19 
  20 #include <drm/drm_fourcc.h>
  21 #include <drm/exynos_drm.h>
  22 
  23 #include "exynos_drm_drv.h"
  24 #include "exynos_drm_ipp.h"
  25 #include "regs-rotator.h"
  26 
  27 /*
  28  * Rotator supports image crop/rotator and input/output DMA operations.
  29  * input DMA reads image data from the memory.
  30  * output DMA writes image data to memory.
  31  */
  32 
  33 #define ROTATOR_AUTOSUSPEND_DELAY       2000
  34 
  35 #define rot_read(offset)        readl(rot->regs + (offset))
  36 #define rot_write(cfg, offset)  writel(cfg, rot->regs + (offset))
  37 
  38 enum rot_irq_status {
  39         ROT_IRQ_STATUS_COMPLETE = 8,
  40         ROT_IRQ_STATUS_ILLEGAL  = 9,
  41 };
  42 
  43 struct rot_variant {
  44         const struct exynos_drm_ipp_formats *formats;
  45         unsigned int    num_formats;
  46 };
  47 
  48 /*
  49  * A structure of rotator context.
  50  * @ippdrv: prepare initialization using ippdrv.
  51  * @regs: memory mapped io registers.
  52  * @clock: rotator gate clock.
  53  * @limit_tbl: limitation of rotator.
  54  * @irq: irq number.
  55  */
  56 struct rot_context {
  57         struct exynos_drm_ipp ipp;
  58         struct drm_device *drm_dev;
  59         void            *dma_priv;
  60         struct device   *dev;
  61         void __iomem    *regs;
  62         struct clk      *clock;
  63         const struct exynos_drm_ipp_formats *formats;
  64         unsigned int    num_formats;
  65         struct exynos_drm_ipp_task      *task;
  66 };
  67 
  68 static void rotator_reg_set_irq(struct rot_context *rot, bool enable)
  69 {
  70         u32 val = rot_read(ROT_CONFIG);
  71 
  72         if (enable == true)
  73                 val |= ROT_CONFIG_IRQ;
  74         else
  75                 val &= ~ROT_CONFIG_IRQ;
  76 
  77         rot_write(val, ROT_CONFIG);
  78 }
  79 
  80 static enum rot_irq_status rotator_reg_get_irq_status(struct rot_context *rot)
  81 {
  82         u32 val = rot_read(ROT_STATUS);
  83 
  84         val = ROT_STATUS_IRQ(val);
  85 
  86         if (val == ROT_STATUS_IRQ_VAL_COMPLETE)
  87                 return ROT_IRQ_STATUS_COMPLETE;
  88 
  89         return ROT_IRQ_STATUS_ILLEGAL;
  90 }
  91 
  92 static irqreturn_t rotator_irq_handler(int irq, void *arg)
  93 {
  94         struct rot_context *rot = arg;
  95         enum rot_irq_status irq_status;
  96         u32 val;
  97 
  98         /* Get execution result */
  99         irq_status = rotator_reg_get_irq_status(rot);
 100 
 101         /* clear status */
 102         val = rot_read(ROT_STATUS);
 103         val |= ROT_STATUS_IRQ_PENDING((u32)irq_status);
 104         rot_write(val, ROT_STATUS);
 105 
 106         if (rot->task) {
 107                 struct exynos_drm_ipp_task *task = rot->task;
 108 
 109                 rot->task = NULL;
 110                 pm_runtime_mark_last_busy(rot->dev);
 111                 pm_runtime_put_autosuspend(rot->dev);
 112                 exynos_drm_ipp_task_done(task,
 113                         irq_status == ROT_IRQ_STATUS_COMPLETE ? 0 : -EINVAL);
 114         }
 115 
 116         return IRQ_HANDLED;
 117 }
 118 
 119 static void rotator_src_set_fmt(struct rot_context *rot, u32 fmt)
 120 {
 121         u32 val;
 122 
 123         val = rot_read(ROT_CONTROL);
 124         val &= ~ROT_CONTROL_FMT_MASK;
 125 
 126         switch (fmt) {
 127         case DRM_FORMAT_NV12:
 128                 val |= ROT_CONTROL_FMT_YCBCR420_2P;
 129                 break;
 130         case DRM_FORMAT_XRGB8888:
 131                 val |= ROT_CONTROL_FMT_RGB888;
 132                 break;
 133         }
 134 
 135         rot_write(val, ROT_CONTROL);
 136 }
 137 
 138 static void rotator_src_set_buf(struct rot_context *rot,
 139                                 struct exynos_drm_ipp_buffer *buf)
 140 {
 141         u32 val;
 142 
 143         /* Set buffer size configuration */
 144         val = ROT_SET_BUF_SIZE_H(buf->buf.height) |
 145               ROT_SET_BUF_SIZE_W(buf->buf.pitch[0] / buf->format->cpp[0]);
 146         rot_write(val, ROT_SRC_BUF_SIZE);
 147 
 148         /* Set crop image position configuration */
 149         val = ROT_CROP_POS_Y(buf->rect.y) | ROT_CROP_POS_X(buf->rect.x);
 150         rot_write(val, ROT_SRC_CROP_POS);
 151         val = ROT_SRC_CROP_SIZE_H(buf->rect.h) |
 152               ROT_SRC_CROP_SIZE_W(buf->rect.w);
 153         rot_write(val, ROT_SRC_CROP_SIZE);
 154 
 155         /* Set buffer DMA address */
 156         rot_write(buf->dma_addr[0], ROT_SRC_BUF_ADDR(0));
 157         rot_write(buf->dma_addr[1], ROT_SRC_BUF_ADDR(1));
 158 }
 159 
 160 static void rotator_dst_set_transf(struct rot_context *rot,
 161                                    unsigned int rotation)
 162 {
 163         u32 val;
 164 
 165         /* Set transform configuration */
 166         val = rot_read(ROT_CONTROL);
 167         val &= ~ROT_CONTROL_FLIP_MASK;
 168 
 169         if (rotation & DRM_MODE_REFLECT_X)
 170                 val |= ROT_CONTROL_FLIP_VERTICAL;
 171         if (rotation & DRM_MODE_REFLECT_Y)
 172                 val |= ROT_CONTROL_FLIP_HORIZONTAL;
 173 
 174         val &= ~ROT_CONTROL_ROT_MASK;
 175 
 176         if (rotation & DRM_MODE_ROTATE_90)
 177                 val |= ROT_CONTROL_ROT_90;
 178         else if (rotation & DRM_MODE_ROTATE_180)
 179                 val |= ROT_CONTROL_ROT_180;
 180         else if (rotation & DRM_MODE_ROTATE_270)
 181                 val |= ROT_CONTROL_ROT_270;
 182 
 183         rot_write(val, ROT_CONTROL);
 184 }
 185 
 186 static void rotator_dst_set_buf(struct rot_context *rot,
 187                                 struct exynos_drm_ipp_buffer *buf)
 188 {
 189         u32 val;
 190 
 191         /* Set buffer size configuration */
 192         val = ROT_SET_BUF_SIZE_H(buf->buf.height) |
 193               ROT_SET_BUF_SIZE_W(buf->buf.pitch[0] / buf->format->cpp[0]);
 194         rot_write(val, ROT_DST_BUF_SIZE);
 195 
 196         /* Set crop image position configuration */
 197         val = ROT_CROP_POS_Y(buf->rect.y) | ROT_CROP_POS_X(buf->rect.x);
 198         rot_write(val, ROT_DST_CROP_POS);
 199 
 200         /* Set buffer DMA address */
 201         rot_write(buf->dma_addr[0], ROT_DST_BUF_ADDR(0));
 202         rot_write(buf->dma_addr[1], ROT_DST_BUF_ADDR(1));
 203 }
 204 
 205 static void rotator_start(struct rot_context *rot)
 206 {
 207         u32 val;
 208 
 209         /* Set interrupt enable */
 210         rotator_reg_set_irq(rot, true);
 211 
 212         val = rot_read(ROT_CONTROL);
 213         val |= ROT_CONTROL_START;
 214         rot_write(val, ROT_CONTROL);
 215 }
 216 
 217 static int rotator_commit(struct exynos_drm_ipp *ipp,
 218                           struct exynos_drm_ipp_task *task)
 219 {
 220         struct rot_context *rot =
 221                         container_of(ipp, struct rot_context, ipp);
 222 
 223         pm_runtime_get_sync(rot->dev);
 224         rot->task = task;
 225 
 226         rotator_src_set_fmt(rot, task->src.buf.fourcc);
 227         rotator_src_set_buf(rot, &task->src);
 228         rotator_dst_set_transf(rot, task->transform.rotation);
 229         rotator_dst_set_buf(rot, &task->dst);
 230         rotator_start(rot);
 231 
 232         return 0;
 233 }
 234 
 235 static const struct exynos_drm_ipp_funcs ipp_funcs = {
 236         .commit = rotator_commit,
 237 };
 238 
 239 static int rotator_bind(struct device *dev, struct device *master, void *data)
 240 {
 241         struct rot_context *rot = dev_get_drvdata(dev);
 242         struct drm_device *drm_dev = data;
 243         struct exynos_drm_ipp *ipp = &rot->ipp;
 244 
 245         rot->drm_dev = drm_dev;
 246         ipp->drm_dev = drm_dev;
 247         exynos_drm_register_dma(drm_dev, dev, &rot->dma_priv);
 248 
 249         exynos_drm_ipp_register(dev, ipp, &ipp_funcs,
 250                            DRM_EXYNOS_IPP_CAP_CROP | DRM_EXYNOS_IPP_CAP_ROTATE,
 251                            rot->formats, rot->num_formats, "rotator");
 252 
 253         dev_info(dev, "The exynos rotator has been probed successfully\n");
 254 
 255         return 0;
 256 }
 257 
 258 static void rotator_unbind(struct device *dev, struct device *master,
 259                         void *data)
 260 {
 261         struct rot_context *rot = dev_get_drvdata(dev);
 262         struct exynos_drm_ipp *ipp = &rot->ipp;
 263 
 264         exynos_drm_ipp_unregister(dev, ipp);
 265         exynos_drm_unregister_dma(rot->drm_dev, rot->dev, &rot->dma_priv);
 266 }
 267 
 268 static const struct component_ops rotator_component_ops = {
 269         .bind   = rotator_bind,
 270         .unbind = rotator_unbind,
 271 };
 272 
 273 static int rotator_probe(struct platform_device *pdev)
 274 {
 275         struct device *dev = &pdev->dev;
 276         struct resource *regs_res;
 277         struct rot_context *rot;
 278         const struct rot_variant *variant;
 279         int irq;
 280         int ret;
 281 
 282         rot = devm_kzalloc(dev, sizeof(*rot), GFP_KERNEL);
 283         if (!rot)
 284                 return -ENOMEM;
 285 
 286         variant = of_device_get_match_data(dev);
 287         rot->formats = variant->formats;
 288         rot->num_formats = variant->num_formats;
 289         rot->dev = dev;
 290         regs_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 291         rot->regs = devm_ioremap_resource(dev, regs_res);
 292         if (IS_ERR(rot->regs))
 293                 return PTR_ERR(rot->regs);
 294 
 295         irq = platform_get_irq(pdev, 0);
 296         if (irq < 0) {
 297                 dev_err(dev, "failed to get irq\n");
 298                 return irq;
 299         }
 300 
 301         ret = devm_request_irq(dev, irq, rotator_irq_handler, 0, dev_name(dev),
 302                                rot);
 303         if (ret < 0) {
 304                 dev_err(dev, "failed to request irq\n");
 305                 return ret;
 306         }
 307 
 308         rot->clock = devm_clk_get(dev, "rotator");
 309         if (IS_ERR(rot->clock)) {
 310                 dev_err(dev, "failed to get clock\n");
 311                 return PTR_ERR(rot->clock);
 312         }
 313 
 314         pm_runtime_use_autosuspend(dev);
 315         pm_runtime_set_autosuspend_delay(dev, ROTATOR_AUTOSUSPEND_DELAY);
 316         pm_runtime_enable(dev);
 317         platform_set_drvdata(pdev, rot);
 318 
 319         ret = component_add(dev, &rotator_component_ops);
 320         if (ret)
 321                 goto err_component;
 322 
 323         return 0;
 324 
 325 err_component:
 326         pm_runtime_dont_use_autosuspend(dev);
 327         pm_runtime_disable(dev);
 328         return ret;
 329 }
 330 
 331 static int rotator_remove(struct platform_device *pdev)
 332 {
 333         struct device *dev = &pdev->dev;
 334 
 335         component_del(dev, &rotator_component_ops);
 336         pm_runtime_dont_use_autosuspend(dev);
 337         pm_runtime_disable(dev);
 338 
 339         return 0;
 340 }
 341 
 342 #ifdef CONFIG_PM
 343 static int rotator_runtime_suspend(struct device *dev)
 344 {
 345         struct rot_context *rot = dev_get_drvdata(dev);
 346 
 347         clk_disable_unprepare(rot->clock);
 348         return 0;
 349 }
 350 
 351 static int rotator_runtime_resume(struct device *dev)
 352 {
 353         struct rot_context *rot = dev_get_drvdata(dev);
 354 
 355         return clk_prepare_enable(rot->clock);
 356 }
 357 #endif
 358 
 359 static const struct drm_exynos_ipp_limit rotator_s5pv210_rbg888_limits[] = {
 360         { IPP_SIZE_LIMIT(BUFFER, .h = { 8, SZ_16K }, .v = { 8, SZ_16K }) },
 361         { IPP_SIZE_LIMIT(AREA, .h.align = 2, .v.align = 2) },
 362 };
 363 
 364 static const struct drm_exynos_ipp_limit rotator_4210_rbg888_limits[] = {
 365         { IPP_SIZE_LIMIT(BUFFER, .h = { 8, SZ_16K }, .v = { 8, SZ_16K }) },
 366         { IPP_SIZE_LIMIT(AREA, .h.align = 4, .v.align = 4) },
 367 };
 368 
 369 static const struct drm_exynos_ipp_limit rotator_4412_rbg888_limits[] = {
 370         { IPP_SIZE_LIMIT(BUFFER, .h = { 8, SZ_8K }, .v = { 8, SZ_8K }) },
 371         { IPP_SIZE_LIMIT(AREA, .h.align = 4, .v.align = 4) },
 372 };
 373 
 374 static const struct drm_exynos_ipp_limit rotator_5250_rbg888_limits[] = {
 375         { IPP_SIZE_LIMIT(BUFFER, .h = { 8, SZ_8K }, .v = { 8, SZ_8K }) },
 376         { IPP_SIZE_LIMIT(AREA, .h.align = 2, .v.align = 2) },
 377 };
 378 
 379 static const struct drm_exynos_ipp_limit rotator_s5pv210_yuv_limits[] = {
 380         { IPP_SIZE_LIMIT(BUFFER, .h = { 32, SZ_64K }, .v = { 32, SZ_64K }) },
 381         { IPP_SIZE_LIMIT(AREA, .h.align = 8, .v.align = 8) },
 382 };
 383 
 384 static const struct drm_exynos_ipp_limit rotator_4210_yuv_limits[] = {
 385         { IPP_SIZE_LIMIT(BUFFER, .h = { 32, SZ_64K }, .v = { 32, SZ_64K }) },
 386         { IPP_SIZE_LIMIT(AREA, .h.align = 8, .v.align = 8) },
 387 };
 388 
 389 static const struct drm_exynos_ipp_limit rotator_4412_yuv_limits[] = {
 390         { IPP_SIZE_LIMIT(BUFFER, .h = { 32, SZ_32K }, .v = { 32, SZ_32K }) },
 391         { IPP_SIZE_LIMIT(AREA, .h.align = 8, .v.align = 8) },
 392 };
 393 
 394 static const struct exynos_drm_ipp_formats rotator_s5pv210_formats[] = {
 395         { IPP_SRCDST_FORMAT(XRGB8888, rotator_s5pv210_rbg888_limits) },
 396         { IPP_SRCDST_FORMAT(NV12, rotator_s5pv210_yuv_limits) },
 397 };
 398 
 399 static const struct exynos_drm_ipp_formats rotator_4210_formats[] = {
 400         { IPP_SRCDST_FORMAT(XRGB8888, rotator_4210_rbg888_limits) },
 401         { IPP_SRCDST_FORMAT(NV12, rotator_4210_yuv_limits) },
 402 };
 403 
 404 static const struct exynos_drm_ipp_formats rotator_4412_formats[] = {
 405         { IPP_SRCDST_FORMAT(XRGB8888, rotator_4412_rbg888_limits) },
 406         { IPP_SRCDST_FORMAT(NV12, rotator_4412_yuv_limits) },
 407 };
 408 
 409 static const struct exynos_drm_ipp_formats rotator_5250_formats[] = {
 410         { IPP_SRCDST_FORMAT(XRGB8888, rotator_5250_rbg888_limits) },
 411         { IPP_SRCDST_FORMAT(NV12, rotator_4412_yuv_limits) },
 412 };
 413 
 414 static const struct rot_variant rotator_s5pv210_data = {
 415         .formats = rotator_s5pv210_formats,
 416         .num_formats = ARRAY_SIZE(rotator_s5pv210_formats),
 417 };
 418 
 419 static const struct rot_variant rotator_4210_data = {
 420         .formats = rotator_4210_formats,
 421         .num_formats = ARRAY_SIZE(rotator_4210_formats),
 422 };
 423 
 424 static const struct rot_variant rotator_4412_data = {
 425         .formats = rotator_4412_formats,
 426         .num_formats = ARRAY_SIZE(rotator_4412_formats),
 427 };
 428 
 429 static const struct rot_variant rotator_5250_data = {
 430         .formats = rotator_5250_formats,
 431         .num_formats = ARRAY_SIZE(rotator_5250_formats),
 432 };
 433 
 434 static const struct of_device_id exynos_rotator_match[] = {
 435         {
 436                 .compatible = "samsung,s5pv210-rotator",
 437                 .data = &rotator_s5pv210_data,
 438         }, {
 439                 .compatible = "samsung,exynos4210-rotator",
 440                 .data = &rotator_4210_data,
 441         }, {
 442                 .compatible = "samsung,exynos4212-rotator",
 443                 .data = &rotator_4412_data,
 444         }, {
 445                 .compatible = "samsung,exynos5250-rotator",
 446                 .data = &rotator_5250_data,
 447         }, {
 448         },
 449 };
 450 MODULE_DEVICE_TABLE(of, exynos_rotator_match);
 451 
 452 static const struct dev_pm_ops rotator_pm_ops = {
 453         SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
 454                                 pm_runtime_force_resume)
 455         SET_RUNTIME_PM_OPS(rotator_runtime_suspend, rotator_runtime_resume,
 456                                                                         NULL)
 457 };
 458 
 459 struct platform_driver rotator_driver = {
 460         .probe          = rotator_probe,
 461         .remove         = rotator_remove,
 462         .driver         = {
 463                 .name   = "exynos-rotator",
 464                 .owner  = THIS_MODULE,
 465                 .pm     = &rotator_pm_ops,
 466                 .of_match_table = exynos_rotator_match,
 467         },
 468 };

/* [<][>][^][v][top][bottom][index][help] */