linux/drivers/gpu/drm/exynos/exynos_drm_scaler.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * Copyright (C) 2017 Samsung Electronics Co.Ltd
   4 * Author:
   5 *      Andrzej Pietrasiewicz <andrzejtp2010@gmail.com>
   6 */
   7
   8#include <linux/clk.h>
   9#include <linux/component.h>
  10#include <linux/err.h>
  11#include <linux/interrupt.h>
  12#include <linux/io.h>
  13#include <linux/kernel.h>
  14#include <linux/of_device.h>
  15#include <linux/platform_device.h>
  16#include <linux/pm_runtime.h>
  17
  18#include <drm/drm_fourcc.h>
  19#include <drm/exynos_drm.h>
  20
  21#include "exynos_drm_drv.h"
  22#include "exynos_drm_fb.h"
  23#include "exynos_drm_ipp.h"
  24#include "regs-scaler.h"
  25
  26#define scaler_read(offset)             readl(scaler->regs + (offset))
  27#define scaler_write(cfg, offset)       writel(cfg, scaler->regs + (offset))
  28#define SCALER_MAX_CLK                  4
  29#define SCALER_AUTOSUSPEND_DELAY        2000
  30#define SCALER_RESET_WAIT_RETRIES       100
  31
  32struct scaler_data {
  33        const char      *clk_name[SCALER_MAX_CLK];
  34        unsigned int    num_clk;
  35        const struct exynos_drm_ipp_formats *formats;
  36        unsigned int    num_formats;
  37};
  38
  39struct scaler_context {
  40        struct exynos_drm_ipp           ipp;
  41        struct drm_device               *drm_dev;
  42        void                            *dma_priv;
  43        struct device                   *dev;
  44        void __iomem                    *regs;
  45        struct clk                      *clock[SCALER_MAX_CLK];
  46        struct exynos_drm_ipp_task      *task;
  47        const struct scaler_data        *scaler_data;
  48};
  49
  50struct scaler_format {
  51        u32     drm_fmt;
  52        u32     internal_fmt;
  53        u32     chroma_tile_w;
  54        u32     chroma_tile_h;
  55};
  56
  57static const struct scaler_format scaler_formats[] = {
  58        { DRM_FORMAT_NV12, SCALER_YUV420_2P_UV, 8, 8 },
  59        { DRM_FORMAT_NV21, SCALER_YUV420_2P_VU, 8, 8 },
  60        { DRM_FORMAT_YUV420, SCALER_YUV420_3P, 8, 8 },
  61        { DRM_FORMAT_YUYV, SCALER_YUV422_1P_YUYV, 16, 16 },
  62        { DRM_FORMAT_UYVY, SCALER_YUV422_1P_UYVY, 16, 16 },
  63        { DRM_FORMAT_YVYU, SCALER_YUV422_1P_YVYU, 16, 16 },
  64        { DRM_FORMAT_NV16, SCALER_YUV422_2P_UV, 8, 16 },
  65        { DRM_FORMAT_NV61, SCALER_YUV422_2P_VU, 8, 16 },
  66        { DRM_FORMAT_YUV422, SCALER_YUV422_3P, 8, 16 },
  67        { DRM_FORMAT_NV24, SCALER_YUV444_2P_UV, 16, 16 },
  68        { DRM_FORMAT_NV42, SCALER_YUV444_2P_VU, 16, 16 },
  69        { DRM_FORMAT_YUV444, SCALER_YUV444_3P, 16, 16 },
  70        { DRM_FORMAT_RGB565, SCALER_RGB_565, 0, 0 },
  71        { DRM_FORMAT_XRGB1555, SCALER_ARGB1555, 0, 0 },
  72        { DRM_FORMAT_ARGB1555, SCALER_ARGB1555, 0, 0 },
  73        { DRM_FORMAT_XRGB4444, SCALER_ARGB4444, 0, 0 },
  74        { DRM_FORMAT_ARGB4444, SCALER_ARGB4444, 0, 0 },
  75        { DRM_FORMAT_XRGB8888, SCALER_ARGB8888, 0, 0 },
  76        { DRM_FORMAT_ARGB8888, SCALER_ARGB8888, 0, 0 },
  77        { DRM_FORMAT_RGBX8888, SCALER_RGBA8888, 0, 0 },
  78        { DRM_FORMAT_RGBA8888, SCALER_RGBA8888, 0, 0 },
  79};
  80
  81static const struct scaler_format *scaler_get_format(u32 drm_fmt)
  82{
  83        int i;
  84
  85        for (i = 0; i < ARRAY_SIZE(scaler_formats); i++)
  86                if (scaler_formats[i].drm_fmt == drm_fmt)
  87                        return &scaler_formats[i];
  88
  89        return NULL;
  90}
  91
  92static inline int scaler_reset(struct scaler_context *scaler)
  93{
  94        int retry = SCALER_RESET_WAIT_RETRIES;
  95
  96        scaler_write(SCALER_CFG_SOFT_RESET, SCALER_CFG);
  97        do {
  98                cpu_relax();
  99        } while (--retry > 1 &&
 100                 scaler_read(SCALER_CFG) & SCALER_CFG_SOFT_RESET);
 101        do {
 102                cpu_relax();
 103                scaler_write(1, SCALER_INT_EN);
 104        } while (--retry > 0 && scaler_read(SCALER_INT_EN) != 1);
 105
 106        return retry ? 0 : -EIO;
 107}
 108
 109static inline void scaler_enable_int(struct scaler_context *scaler)
 110{
 111        u32 val;
 112
 113        val = SCALER_INT_EN_TIMEOUT |
 114                SCALER_INT_EN_ILLEGAL_BLEND |
 115                SCALER_INT_EN_ILLEGAL_RATIO |
 116                SCALER_INT_EN_ILLEGAL_DST_HEIGHT |
 117                SCALER_INT_EN_ILLEGAL_DST_WIDTH |
 118                SCALER_INT_EN_ILLEGAL_DST_V_POS |
 119                SCALER_INT_EN_ILLEGAL_DST_H_POS |
 120                SCALER_INT_EN_ILLEGAL_DST_C_SPAN |
 121                SCALER_INT_EN_ILLEGAL_DST_Y_SPAN |
 122                SCALER_INT_EN_ILLEGAL_DST_CR_BASE |
 123                SCALER_INT_EN_ILLEGAL_DST_CB_BASE |
 124                SCALER_INT_EN_ILLEGAL_DST_Y_BASE |
 125                SCALER_INT_EN_ILLEGAL_DST_COLOR |
 126                SCALER_INT_EN_ILLEGAL_SRC_HEIGHT |
 127                SCALER_INT_EN_ILLEGAL_SRC_WIDTH |
 128                SCALER_INT_EN_ILLEGAL_SRC_CV_POS |
 129                SCALER_INT_EN_ILLEGAL_SRC_CH_POS |
 130                SCALER_INT_EN_ILLEGAL_SRC_YV_POS |
 131                SCALER_INT_EN_ILLEGAL_SRC_YH_POS |
 132                SCALER_INT_EN_ILLEGAL_DST_SPAN |
 133                SCALER_INT_EN_ILLEGAL_SRC_Y_SPAN |
 134                SCALER_INT_EN_ILLEGAL_SRC_CR_BASE |
 135                SCALER_INT_EN_ILLEGAL_SRC_CB_BASE |
 136                SCALER_INT_EN_ILLEGAL_SRC_Y_BASE |
 137                SCALER_INT_EN_ILLEGAL_SRC_COLOR |
 138                SCALER_INT_EN_FRAME_END;
 139        scaler_write(val, SCALER_INT_EN);
 140}
 141
 142static inline void scaler_set_src_fmt(struct scaler_context *scaler,
 143        u32 src_fmt, u32 tile)
 144{
 145        u32 val;
 146
 147        val = SCALER_SRC_CFG_SET_COLOR_FORMAT(src_fmt) | (tile << 10);
 148        scaler_write(val, SCALER_SRC_CFG);
 149}
 150
 151static inline void scaler_set_src_base(struct scaler_context *scaler,
 152        struct exynos_drm_ipp_buffer *src_buf)
 153{
 154        static unsigned int bases[] = {
 155                SCALER_SRC_Y_BASE,
 156                SCALER_SRC_CB_BASE,
 157                SCALER_SRC_CR_BASE,
 158        };
 159        int i;
 160
 161        for (i = 0; i < src_buf->format->num_planes; ++i)
 162                scaler_write(src_buf->dma_addr[i], bases[i]);
 163}
 164
 165static inline void scaler_set_src_span(struct scaler_context *scaler,
 166        struct exynos_drm_ipp_buffer *src_buf)
 167{
 168        u32 val;
 169
 170        val = SCALER_SRC_SPAN_SET_Y_SPAN(src_buf->buf.pitch[0] /
 171                src_buf->format->cpp[0]);
 172
 173        if (src_buf->format->num_planes > 1)
 174                val |= SCALER_SRC_SPAN_SET_C_SPAN(src_buf->buf.pitch[1]);
 175
 176        scaler_write(val, SCALER_SRC_SPAN);
 177}
 178
 179static inline void scaler_set_src_luma_chroma_pos(struct scaler_context *scaler,
 180                        struct drm_exynos_ipp_task_rect *src_pos,
 181                        const struct scaler_format *fmt)
 182{
 183        u32 val;
 184
 185        val = SCALER_SRC_Y_POS_SET_YH_POS(src_pos->x << 2);
 186        val |=  SCALER_SRC_Y_POS_SET_YV_POS(src_pos->y << 2);
 187        scaler_write(val, SCALER_SRC_Y_POS);
 188        val = SCALER_SRC_C_POS_SET_CH_POS(
 189                (src_pos->x * fmt->chroma_tile_w / 16) << 2);
 190        val |=  SCALER_SRC_C_POS_SET_CV_POS(
 191                (src_pos->y * fmt->chroma_tile_h / 16) << 2);
 192        scaler_write(val, SCALER_SRC_C_POS);
 193}
 194
 195static inline void scaler_set_src_wh(struct scaler_context *scaler,
 196        struct drm_exynos_ipp_task_rect *src_pos)
 197{
 198        u32 val;
 199
 200        val = SCALER_SRC_WH_SET_WIDTH(src_pos->w);
 201        val |= SCALER_SRC_WH_SET_HEIGHT(src_pos->h);
 202        scaler_write(val, SCALER_SRC_WH);
 203}
 204
 205static inline void scaler_set_dst_fmt(struct scaler_context *scaler,
 206        u32 dst_fmt)
 207{
 208        u32 val;
 209
 210        val = SCALER_DST_CFG_SET_COLOR_FORMAT(dst_fmt);
 211        scaler_write(val, SCALER_DST_CFG);
 212}
 213
 214static inline void scaler_set_dst_base(struct scaler_context *scaler,
 215        struct exynos_drm_ipp_buffer *dst_buf)
 216{
 217        static unsigned int bases[] = {
 218                SCALER_DST_Y_BASE,
 219                SCALER_DST_CB_BASE,
 220                SCALER_DST_CR_BASE,
 221        };
 222        int i;
 223
 224        for (i = 0; i < dst_buf->format->num_planes; ++i)
 225                scaler_write(dst_buf->dma_addr[i], bases[i]);
 226}
 227
 228static inline void scaler_set_dst_span(struct scaler_context *scaler,
 229        struct exynos_drm_ipp_buffer *dst_buf)
 230{
 231        u32 val;
 232
 233        val = SCALER_DST_SPAN_SET_Y_SPAN(dst_buf->buf.pitch[0] /
 234                dst_buf->format->cpp[0]);
 235
 236        if (dst_buf->format->num_planes > 1)
 237                val |= SCALER_DST_SPAN_SET_C_SPAN(dst_buf->buf.pitch[1]);
 238
 239        scaler_write(val, SCALER_DST_SPAN);
 240}
 241
 242static inline void scaler_set_dst_luma_pos(struct scaler_context *scaler,
 243        struct drm_exynos_ipp_task_rect *dst_pos)
 244{
 245        u32 val;
 246
 247        val = SCALER_DST_WH_SET_WIDTH(dst_pos->w);
 248        val |= SCALER_DST_WH_SET_HEIGHT(dst_pos->h);
 249        scaler_write(val, SCALER_DST_WH);
 250}
 251
 252static inline void scaler_set_dst_wh(struct scaler_context *scaler,
 253        struct drm_exynos_ipp_task_rect *dst_pos)
 254{
 255        u32 val;
 256
 257        val = SCALER_DST_POS_SET_H_POS(dst_pos->x);
 258        val |= SCALER_DST_POS_SET_V_POS(dst_pos->y);
 259        scaler_write(val, SCALER_DST_POS);
 260}
 261
 262static inline void scaler_set_hv_ratio(struct scaler_context *scaler,
 263        unsigned int rotation,
 264        struct drm_exynos_ipp_task_rect *src_pos,
 265        struct drm_exynos_ipp_task_rect *dst_pos)
 266{
 267        u32 val, h_ratio, v_ratio;
 268
 269        if (drm_rotation_90_or_270(rotation)) {
 270                h_ratio = (src_pos->h << 16) / dst_pos->w;
 271                v_ratio = (src_pos->w << 16) / dst_pos->h;
 272        } else {
 273                h_ratio = (src_pos->w << 16) / dst_pos->w;
 274                v_ratio = (src_pos->h << 16) / dst_pos->h;
 275        }
 276
 277        val = SCALER_H_RATIO_SET(h_ratio);
 278        scaler_write(val, SCALER_H_RATIO);
 279
 280        val = SCALER_V_RATIO_SET(v_ratio);
 281        scaler_write(val, SCALER_V_RATIO);
 282}
 283
 284static inline void scaler_set_rotation(struct scaler_context *scaler,
 285        unsigned int rotation)
 286{
 287        u32 val = 0;
 288
 289        if (rotation & DRM_MODE_ROTATE_90)
 290                val |= SCALER_ROT_CFG_SET_ROTMODE(SCALER_ROT_MODE_90);
 291        else if (rotation & DRM_MODE_ROTATE_180)
 292                val |= SCALER_ROT_CFG_SET_ROTMODE(SCALER_ROT_MODE_180);
 293        else if (rotation & DRM_MODE_ROTATE_270)
 294                val |= SCALER_ROT_CFG_SET_ROTMODE(SCALER_ROT_MODE_270);
 295        if (rotation & DRM_MODE_REFLECT_X)
 296                val |= SCALER_ROT_CFG_FLIP_X_EN;
 297        if (rotation & DRM_MODE_REFLECT_Y)
 298                val |= SCALER_ROT_CFG_FLIP_Y_EN;
 299        scaler_write(val, SCALER_ROT_CFG);
 300}
 301
 302static inline void scaler_set_csc(struct scaler_context *scaler,
 303        const struct drm_format_info *fmt)
 304{
 305        static const u32 csc_mtx[2][3][3] = {
 306                { /* YCbCr to RGB */
 307                        {0x254, 0x000, 0x331},
 308                        {0x254, 0xf38, 0xe60},
 309                        {0x254, 0x409, 0x000},
 310                },
 311                { /* RGB to YCbCr */
 312                        {0x084, 0x102, 0x032},
 313                        {0xfb4, 0xf6b, 0x0e1},
 314                        {0x0e1, 0xf44, 0xfdc},
 315                },
 316        };
 317        int i, j, dir;
 318
 319        switch (fmt->format) {
 320        case DRM_FORMAT_RGB565:
 321        case DRM_FORMAT_XRGB1555:
 322        case DRM_FORMAT_ARGB1555:
 323        case DRM_FORMAT_XRGB4444:
 324        case DRM_FORMAT_ARGB4444:
 325        case DRM_FORMAT_XRGB8888:
 326        case DRM_FORMAT_ARGB8888:
 327        case DRM_FORMAT_RGBX8888:
 328        case DRM_FORMAT_RGBA8888:
 329                dir = 1;
 330                break;
 331        default:
 332                dir = 0;
 333        }
 334
 335        for (i = 0; i < 3; i++)
 336                for (j = 0; j < 3; j++)
 337                        scaler_write(csc_mtx[dir][i][j], SCALER_CSC_COEF(j, i));
 338}
 339
 340static inline void scaler_set_timer(struct scaler_context *scaler,
 341        unsigned int timer, unsigned int divider)
 342{
 343        u32 val;
 344
 345        val = SCALER_TIMEOUT_CTRL_TIMER_ENABLE;
 346        val |= SCALER_TIMEOUT_CTRL_SET_TIMER_VALUE(timer);
 347        val |= SCALER_TIMEOUT_CTRL_SET_TIMER_DIV(divider);
 348        scaler_write(val, SCALER_TIMEOUT_CTRL);
 349}
 350
 351static inline void scaler_start_hw(struct scaler_context *scaler)
 352{
 353        scaler_write(SCALER_CFG_START_CMD, SCALER_CFG);
 354}
 355
 356static int scaler_commit(struct exynos_drm_ipp *ipp,
 357                          struct exynos_drm_ipp_task *task)
 358{
 359        struct scaler_context *scaler =
 360                        container_of(ipp, struct scaler_context, ipp);
 361
 362        struct drm_exynos_ipp_task_rect *src_pos = &task->src.rect;
 363        struct drm_exynos_ipp_task_rect *dst_pos = &task->dst.rect;
 364        const struct scaler_format *src_fmt, *dst_fmt;
 365        int ret = 0;
 366
 367        src_fmt = scaler_get_format(task->src.buf.fourcc);
 368        dst_fmt = scaler_get_format(task->dst.buf.fourcc);
 369
 370        ret = pm_runtime_resume_and_get(scaler->dev);
 371        if (ret < 0)
 372                return ret;
 373
 374        if (scaler_reset(scaler))
 375                return -EIO;
 376
 377        scaler->task = task;
 378
 379        scaler_set_src_fmt(
 380                scaler, src_fmt->internal_fmt, task->src.buf.modifier != 0);
 381        scaler_set_src_base(scaler, &task->src);
 382        scaler_set_src_span(scaler, &task->src);
 383        scaler_set_src_luma_chroma_pos(scaler, src_pos, src_fmt);
 384        scaler_set_src_wh(scaler, src_pos);
 385
 386        scaler_set_dst_fmt(scaler, dst_fmt->internal_fmt);
 387        scaler_set_dst_base(scaler, &task->dst);
 388        scaler_set_dst_span(scaler, &task->dst);
 389        scaler_set_dst_luma_pos(scaler, dst_pos);
 390        scaler_set_dst_wh(scaler, dst_pos);
 391
 392        scaler_set_hv_ratio(scaler, task->transform.rotation, src_pos, dst_pos);
 393        scaler_set_rotation(scaler, task->transform.rotation);
 394
 395        scaler_set_csc(scaler, task->src.format);
 396
 397        scaler_set_timer(scaler, 0xffff, 0xf);
 398
 399        scaler_enable_int(scaler);
 400        scaler_start_hw(scaler);
 401
 402        return 0;
 403}
 404
 405static struct exynos_drm_ipp_funcs ipp_funcs = {
 406        .commit = scaler_commit,
 407};
 408
 409static inline void scaler_disable_int(struct scaler_context *scaler)
 410{
 411        scaler_write(0, SCALER_INT_EN);
 412}
 413
 414static inline u32 scaler_get_int_status(struct scaler_context *scaler)
 415{
 416        u32 val = scaler_read(SCALER_INT_STATUS);
 417
 418        scaler_write(val, SCALER_INT_STATUS);
 419
 420        return val;
 421}
 422
 423static inline int scaler_task_done(u32 val)
 424{
 425        return val & SCALER_INT_STATUS_FRAME_END ? 0 : -EINVAL;
 426}
 427
 428static irqreturn_t scaler_irq_handler(int irq, void *arg)
 429{
 430        struct scaler_context *scaler = arg;
 431
 432        u32 val = scaler_get_int_status(scaler);
 433
 434        scaler_disable_int(scaler);
 435
 436        if (scaler->task) {
 437                struct exynos_drm_ipp_task *task = scaler->task;
 438
 439                scaler->task = NULL;
 440                pm_runtime_mark_last_busy(scaler->dev);
 441                pm_runtime_put_autosuspend(scaler->dev);
 442                exynos_drm_ipp_task_done(task, scaler_task_done(val));
 443        }
 444
 445        return IRQ_HANDLED;
 446}
 447
 448static int scaler_bind(struct device *dev, struct device *master, void *data)
 449{
 450        struct scaler_context *scaler = dev_get_drvdata(dev);
 451        struct drm_device *drm_dev = data;
 452        struct exynos_drm_ipp *ipp = &scaler->ipp;
 453
 454        scaler->drm_dev = drm_dev;
 455        ipp->drm_dev = drm_dev;
 456        exynos_drm_register_dma(drm_dev, dev, &scaler->dma_priv);
 457
 458        exynos_drm_ipp_register(dev, ipp, &ipp_funcs,
 459                        DRM_EXYNOS_IPP_CAP_CROP | DRM_EXYNOS_IPP_CAP_ROTATE |
 460                        DRM_EXYNOS_IPP_CAP_SCALE | DRM_EXYNOS_IPP_CAP_CONVERT,
 461                        scaler->scaler_data->formats,
 462                        scaler->scaler_data->num_formats, "scaler");
 463
 464        dev_info(dev, "The exynos scaler has been probed successfully\n");
 465
 466        return 0;
 467}
 468
 469static void scaler_unbind(struct device *dev, struct device *master,
 470                        void *data)
 471{
 472        struct scaler_context *scaler = dev_get_drvdata(dev);
 473        struct exynos_drm_ipp *ipp = &scaler->ipp;
 474
 475        exynos_drm_ipp_unregister(dev, ipp);
 476        exynos_drm_unregister_dma(scaler->drm_dev, scaler->dev,
 477                                  &scaler->dma_priv);
 478}
 479
 480static const struct component_ops scaler_component_ops = {
 481        .bind   = scaler_bind,
 482        .unbind = scaler_unbind,
 483};
 484
 485static int scaler_probe(struct platform_device *pdev)
 486{
 487        struct device *dev = &pdev->dev;
 488        struct resource *regs_res;
 489        struct scaler_context *scaler;
 490        int irq;
 491        int ret, i;
 492
 493        scaler = devm_kzalloc(dev, sizeof(*scaler), GFP_KERNEL);
 494        if (!scaler)
 495                return -ENOMEM;
 496
 497        scaler->scaler_data =
 498                (struct scaler_data *)of_device_get_match_data(dev);
 499
 500        scaler->dev = dev;
 501        regs_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 502        scaler->regs = devm_ioremap_resource(dev, regs_res);
 503        if (IS_ERR(scaler->regs))
 504                return PTR_ERR(scaler->regs);
 505
 506        irq = platform_get_irq(pdev, 0);
 507        if (irq < 0)
 508                return irq;
 509
 510        ret = devm_request_threaded_irq(dev, irq, NULL, scaler_irq_handler,
 511                                        IRQF_ONESHOT, "drm_scaler", scaler);
 512        if (ret < 0) {
 513                dev_err(dev, "failed to request irq\n");
 514                return ret;
 515        }
 516
 517        for (i = 0; i < scaler->scaler_data->num_clk; ++i) {
 518                scaler->clock[i] = devm_clk_get(dev,
 519                                              scaler->scaler_data->clk_name[i]);
 520                if (IS_ERR(scaler->clock[i])) {
 521                        dev_err(dev, "failed to get clock\n");
 522                        return PTR_ERR(scaler->clock[i]);
 523                }
 524        }
 525
 526        pm_runtime_use_autosuspend(dev);
 527        pm_runtime_set_autosuspend_delay(dev, SCALER_AUTOSUSPEND_DELAY);
 528        pm_runtime_enable(dev);
 529        platform_set_drvdata(pdev, scaler);
 530
 531        ret = component_add(dev, &scaler_component_ops);
 532        if (ret)
 533                goto err_ippdrv_register;
 534
 535        return 0;
 536
 537err_ippdrv_register:
 538        pm_runtime_dont_use_autosuspend(dev);
 539        pm_runtime_disable(dev);
 540        return ret;
 541}
 542
 543static int scaler_remove(struct platform_device *pdev)
 544{
 545        struct device *dev = &pdev->dev;
 546
 547        component_del(dev, &scaler_component_ops);
 548        pm_runtime_dont_use_autosuspend(dev);
 549        pm_runtime_disable(dev);
 550
 551        return 0;
 552}
 553
 554#ifdef CONFIG_PM
 555
 556static int clk_disable_unprepare_wrapper(struct clk *clk)
 557{
 558        clk_disable_unprepare(clk);
 559
 560        return 0;
 561}
 562
 563static int scaler_clk_ctrl(struct scaler_context *scaler, bool enable)
 564{
 565        int (*clk_fun)(struct clk *clk), i;
 566
 567        clk_fun = enable ? clk_prepare_enable : clk_disable_unprepare_wrapper;
 568
 569        for (i = 0; i < scaler->scaler_data->num_clk; ++i)
 570                clk_fun(scaler->clock[i]);
 571
 572        return 0;
 573}
 574
 575static int scaler_runtime_suspend(struct device *dev)
 576{
 577        struct scaler_context *scaler = dev_get_drvdata(dev);
 578
 579        return  scaler_clk_ctrl(scaler, false);
 580}
 581
 582static int scaler_runtime_resume(struct device *dev)
 583{
 584        struct scaler_context *scaler = dev_get_drvdata(dev);
 585
 586        return  scaler_clk_ctrl(scaler, true);
 587}
 588#endif
 589
 590static const struct dev_pm_ops scaler_pm_ops = {
 591        SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
 592                                pm_runtime_force_resume)
 593        SET_RUNTIME_PM_OPS(scaler_runtime_suspend, scaler_runtime_resume, NULL)
 594};
 595
 596static const struct drm_exynos_ipp_limit scaler_5420_two_pixel_hv_limits[] = {
 597        { IPP_SIZE_LIMIT(BUFFER, .h = { 16, SZ_8K }, .v = { 16, SZ_8K }) },
 598        { IPP_SIZE_LIMIT(AREA, .h.align = 2, .v.align = 2) },
 599        { IPP_SCALE_LIMIT(.h = { 65536 * 1 / 4, 65536 * 16 },
 600                          .v = { 65536 * 1 / 4, 65536 * 16 }) },
 601};
 602
 603static const struct drm_exynos_ipp_limit scaler_5420_two_pixel_h_limits[] = {
 604        { IPP_SIZE_LIMIT(BUFFER, .h = { 16, SZ_8K }, .v = { 16, SZ_8K }) },
 605        { IPP_SIZE_LIMIT(AREA, .h.align = 2, .v.align = 1) },
 606        { IPP_SCALE_LIMIT(.h = { 65536 * 1 / 4, 65536 * 16 },
 607                          .v = { 65536 * 1 / 4, 65536 * 16 }) },
 608};
 609
 610static const struct drm_exynos_ipp_limit scaler_5420_one_pixel_limits[] = {
 611        { IPP_SIZE_LIMIT(BUFFER, .h = { 16, SZ_8K }, .v = { 16, SZ_8K }) },
 612        { IPP_SCALE_LIMIT(.h = { 65536 * 1 / 4, 65536 * 16 },
 613                          .v = { 65536 * 1 / 4, 65536 * 16 }) },
 614};
 615
 616static const struct drm_exynos_ipp_limit scaler_5420_tile_limits[] = {
 617        { IPP_SIZE_LIMIT(BUFFER, .h = { 16, SZ_8K }, .v = { 16, SZ_8K })},
 618        { IPP_SIZE_LIMIT(AREA, .h.align = 16, .v.align = 16) },
 619        { IPP_SCALE_LIMIT(.h = {1, 1}, .v = {1, 1})},
 620        { }
 621};
 622
 623#define IPP_SRCDST_TILE_FORMAT(f, l)    \
 624        IPP_SRCDST_MFORMAT(f, DRM_FORMAT_MOD_SAMSUNG_16_16_TILE, (l))
 625
 626static const struct exynos_drm_ipp_formats exynos5420_formats[] = {
 627        /* SCALER_YUV420_2P_UV */
 628        { IPP_SRCDST_FORMAT(NV21, scaler_5420_two_pixel_hv_limits) },
 629
 630        /* SCALER_YUV420_2P_VU */
 631        { IPP_SRCDST_FORMAT(NV12, scaler_5420_two_pixel_hv_limits) },
 632
 633        /* SCALER_YUV420_3P */
 634        { IPP_SRCDST_FORMAT(YUV420, scaler_5420_two_pixel_hv_limits) },
 635
 636        /* SCALER_YUV422_1P_YUYV */
 637        { IPP_SRCDST_FORMAT(YUYV, scaler_5420_two_pixel_h_limits) },
 638
 639        /* SCALER_YUV422_1P_UYVY */
 640        { IPP_SRCDST_FORMAT(UYVY, scaler_5420_two_pixel_h_limits) },
 641
 642        /* SCALER_YUV422_1P_YVYU */
 643        { IPP_SRCDST_FORMAT(YVYU, scaler_5420_two_pixel_h_limits) },
 644
 645        /* SCALER_YUV422_2P_UV */
 646        { IPP_SRCDST_FORMAT(NV61, scaler_5420_two_pixel_h_limits) },
 647
 648        /* SCALER_YUV422_2P_VU */
 649        { IPP_SRCDST_FORMAT(NV16, scaler_5420_two_pixel_h_limits) },
 650
 651        /* SCALER_YUV422_3P */
 652        { IPP_SRCDST_FORMAT(YUV422, scaler_5420_two_pixel_h_limits) },
 653
 654        /* SCALER_YUV444_2P_UV */
 655        { IPP_SRCDST_FORMAT(NV42, scaler_5420_one_pixel_limits) },
 656
 657        /* SCALER_YUV444_2P_VU */
 658        { IPP_SRCDST_FORMAT(NV24, scaler_5420_one_pixel_limits) },
 659
 660        /* SCALER_YUV444_3P */
 661        { IPP_SRCDST_FORMAT(YUV444, scaler_5420_one_pixel_limits) },
 662
 663        /* SCALER_RGB_565 */
 664        { IPP_SRCDST_FORMAT(RGB565, scaler_5420_one_pixel_limits) },
 665
 666        /* SCALER_ARGB1555 */
 667        { IPP_SRCDST_FORMAT(XRGB1555, scaler_5420_one_pixel_limits) },
 668
 669        /* SCALER_ARGB1555 */
 670        { IPP_SRCDST_FORMAT(ARGB1555, scaler_5420_one_pixel_limits) },
 671
 672        /* SCALER_ARGB4444 */
 673        { IPP_SRCDST_FORMAT(XRGB4444, scaler_5420_one_pixel_limits) },
 674
 675        /* SCALER_ARGB4444 */
 676        { IPP_SRCDST_FORMAT(ARGB4444, scaler_5420_one_pixel_limits) },
 677
 678        /* SCALER_ARGB8888 */
 679        { IPP_SRCDST_FORMAT(XRGB8888, scaler_5420_one_pixel_limits) },
 680
 681        /* SCALER_ARGB8888 */
 682        { IPP_SRCDST_FORMAT(ARGB8888, scaler_5420_one_pixel_limits) },
 683
 684        /* SCALER_RGBA8888 */
 685        { IPP_SRCDST_FORMAT(RGBX8888, scaler_5420_one_pixel_limits) },
 686
 687        /* SCALER_RGBA8888 */
 688        { IPP_SRCDST_FORMAT(RGBA8888, scaler_5420_one_pixel_limits) },
 689
 690        /* SCALER_YUV420_2P_UV TILE */
 691        { IPP_SRCDST_TILE_FORMAT(NV21, scaler_5420_tile_limits) },
 692
 693        /* SCALER_YUV420_2P_VU TILE */
 694        { IPP_SRCDST_TILE_FORMAT(NV12, scaler_5420_tile_limits) },
 695
 696        /* SCALER_YUV420_3P TILE */
 697        { IPP_SRCDST_TILE_FORMAT(YUV420, scaler_5420_tile_limits) },
 698
 699        /* SCALER_YUV422_1P_YUYV TILE */
 700        { IPP_SRCDST_TILE_FORMAT(YUYV, scaler_5420_tile_limits) },
 701};
 702
 703static const struct scaler_data exynos5420_data = {
 704        .clk_name       = {"mscl"},
 705        .num_clk        = 1,
 706        .formats        = exynos5420_formats,
 707        .num_formats    = ARRAY_SIZE(exynos5420_formats),
 708};
 709
 710static const struct scaler_data exynos5433_data = {
 711        .clk_name       = {"pclk", "aclk", "aclk_xiu"},
 712        .num_clk        = 3,
 713        .formats        = exynos5420_formats, /* intentional */
 714        .num_formats    = ARRAY_SIZE(exynos5420_formats),
 715};
 716
 717static const struct of_device_id exynos_scaler_match[] = {
 718        {
 719                .compatible = "samsung,exynos5420-scaler",
 720                .data = &exynos5420_data,
 721        }, {
 722                .compatible = "samsung,exynos5433-scaler",
 723                .data = &exynos5433_data,
 724        }, {
 725        },
 726};
 727MODULE_DEVICE_TABLE(of, exynos_scaler_match);
 728
 729struct platform_driver scaler_driver = {
 730        .probe          = scaler_probe,
 731        .remove         = scaler_remove,
 732        .driver         = {
 733                .name   = "exynos-scaler",
 734                .owner  = THIS_MODULE,
 735                .pm     = &scaler_pm_ops,
 736                .of_match_table = exynos_scaler_match,
 737        },
 738};
 739