linux/drivers/gpu/drm/ingenic/ingenic-ipu.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2//
   3// Ingenic JZ47xx IPU driver
   4//
   5// Copyright (C) 2020, Paul Cercueil <paul@crapouillou.net>
   6// Copyright (C) 2020, Daniel Silsby <dansilsby@gmail.com>
   7
   8#include "ingenic-drm.h"
   9#include "ingenic-ipu.h"
  10
  11#include <linux/clk.h>
  12#include <linux/component.h>
  13#include <linux/gcd.h>
  14#include <linux/interrupt.h>
  15#include <linux/module.h>
  16#include <linux/of.h>
  17#include <linux/of_device.h>
  18#include <linux/regmap.h>
  19#include <linux/time.h>
  20
  21#include <drm/drm_atomic.h>
  22#include <drm/drm_atomic_helper.h>
  23#include <drm/drm_drv.h>
  24#include <drm/drm_fb_cma_helper.h>
  25#include <drm/drm_fourcc.h>
  26#include <drm/drm_gem_framebuffer_helper.h>
  27#include <drm/drm_plane.h>
  28#include <drm/drm_plane_helper.h>
  29#include <drm/drm_property.h>
  30#include <drm/drm_vblank.h>
  31
  32struct ingenic_ipu;
  33
  34struct soc_info {
  35        const u32 *formats;
  36        size_t num_formats;
  37        bool has_bicubic;
  38        bool manual_restart;
  39
  40        void (*set_coefs)(struct ingenic_ipu *ipu, unsigned int reg,
  41                          unsigned int sharpness, bool downscale,
  42                          unsigned int weight, unsigned int offset);
  43};
  44
  45struct ingenic_ipu {
  46        struct drm_plane plane;
  47        struct drm_device *drm;
  48        struct device *dev, *master;
  49        struct regmap *map;
  50        struct clk *clk;
  51        const struct soc_info *soc_info;
  52        bool clk_enabled;
  53
  54        unsigned int num_w, num_h, denom_w, denom_h;
  55
  56        dma_addr_t addr_y, addr_u, addr_v;
  57
  58        struct drm_property *sharpness_prop;
  59        unsigned int sharpness;
  60};
  61
  62/* Signed 15.16 fixed-point math (for bicubic scaling coefficients) */
  63#define I2F(i) ((s32)(i) * 65536)
  64#define F2I(f) ((f) / 65536)
  65#define FMUL(fa, fb) ((s32)(((s64)(fa) * (s64)(fb)) / 65536))
  66#define SHARPNESS_INCR (I2F(-1) / 8)
  67
  68static inline struct ingenic_ipu *plane_to_ingenic_ipu(struct drm_plane *plane)
  69{
  70        return container_of(plane, struct ingenic_ipu, plane);
  71}
  72
  73/*
  74 * Apply conventional cubic convolution kernel. Both parameters
  75 *  and return value are 15.16 signed fixed-point.
  76 *
  77 *  @f_a: Sharpness factor, typically in range [-4.0, -0.25].
  78 *        A larger magnitude increases perceived sharpness, but going past
  79 *        -2.0 might cause ringing artifacts to outweigh any improvement.
  80 *        Nice values on a 320x240 LCD are between -0.75 and -2.0.
  81 *
  82 *  @f_x: Absolute distance in pixels from 'pixel 0' sample position
  83 *        along horizontal (or vertical) source axis. Range is [0, +2.0].
  84 *
  85 *  returns: Weight of this pixel within 4-pixel sample group. Range is
  86 *           [-2.0, +2.0]. For moderate (i.e. > -3.0) sharpness factors,
  87 *           range is within [-1.0, +1.0].
  88 */
  89static inline s32 cubic_conv(s32 f_a, s32 f_x)
  90{
  91        const s32 f_1 = I2F(1);
  92        const s32 f_2 = I2F(2);
  93        const s32 f_3 = I2F(3);
  94        const s32 f_4 = I2F(4);
  95        const s32 f_x2 = FMUL(f_x, f_x);
  96        const s32 f_x3 = FMUL(f_x, f_x2);
  97
  98        if (f_x <= f_1)
  99                return FMUL((f_a + f_2), f_x3) - FMUL((f_a + f_3), f_x2) + f_1;
 100        else if (f_x <= f_2)
 101                return FMUL(f_a, (f_x3 - 5 * f_x2 + 8 * f_x - f_4));
 102        else
 103                return 0;
 104}
 105
 106/*
 107 * On entry, "weight" is a coefficient suitable for bilinear mode,
 108 *  which is converted to a set of four suitable for bicubic mode.
 109 *
 110 * "weight 512" means all of pixel 0;
 111 * "weight 256" means half of pixel 0 and half of pixel 1;
 112 * "weight 0" means all of pixel 1;
 113 *
 114 * "offset" is increment to next source pixel sample location.
 115 */
 116static void jz4760_set_coefs(struct ingenic_ipu *ipu, unsigned int reg,
 117                             unsigned int sharpness, bool downscale,
 118                             unsigned int weight, unsigned int offset)
 119{
 120        u32 val;
 121        s32 w0, w1, w2, w3; /* Pixel weights at X (or Y) offsets -1,0,1,2 */
 122
 123        weight = clamp_val(weight, 0, 512);
 124
 125        if (sharpness < 2) {
 126                /*
 127                 *  When sharpness setting is 0, emulate nearest-neighbor.
 128                 *  When sharpness setting is 1, emulate bilinear.
 129                 */
 130
 131                if (sharpness == 0)
 132                        weight = weight >= 256 ? 512 : 0;
 133                w0 = 0;
 134                w1 = weight;
 135                w2 = 512 - weight;
 136                w3 = 0;
 137        } else {
 138                const s32 f_a = SHARPNESS_INCR * sharpness;
 139                const s32 f_h = I2F(1) / 2; /* Round up 0.5 */
 140
 141                /*
 142                 * Note that always rounding towards +infinity here is intended.
 143                 * The resulting coefficients match a round-to-nearest-int
 144                 * double floating-point implementation.
 145                 */
 146
 147                weight = 512 - weight;
 148                w0 = F2I(f_h + 512 * cubic_conv(f_a, I2F(512  + weight) / 512));
 149                w1 = F2I(f_h + 512 * cubic_conv(f_a, I2F(0    + weight) / 512));
 150                w2 = F2I(f_h + 512 * cubic_conv(f_a, I2F(512  - weight) / 512));
 151                w3 = F2I(f_h + 512 * cubic_conv(f_a, I2F(1024 - weight) / 512));
 152                w0 = clamp_val(w0, -1024, 1023);
 153                w1 = clamp_val(w1, -1024, 1023);
 154                w2 = clamp_val(w2, -1024, 1023);
 155                w3 = clamp_val(w3, -1024, 1023);
 156        }
 157
 158        val = ((w1 & JZ4760_IPU_RSZ_COEF_MASK) << JZ4760_IPU_RSZ_COEF31_LSB) |
 159                ((w0 & JZ4760_IPU_RSZ_COEF_MASK) << JZ4760_IPU_RSZ_COEF20_LSB);
 160        regmap_write(ipu->map, reg, val);
 161
 162        val = ((w3 & JZ4760_IPU_RSZ_COEF_MASK) << JZ4760_IPU_RSZ_COEF31_LSB) |
 163                ((w2 & JZ4760_IPU_RSZ_COEF_MASK) << JZ4760_IPU_RSZ_COEF20_LSB) |
 164                ((offset & JZ4760_IPU_RSZ_OFFSET_MASK) << JZ4760_IPU_RSZ_OFFSET_LSB);
 165        regmap_write(ipu->map, reg, val);
 166}
 167
 168static void jz4725b_set_coefs(struct ingenic_ipu *ipu, unsigned int reg,
 169                              unsigned int sharpness, bool downscale,
 170                              unsigned int weight, unsigned int offset)
 171{
 172        u32 val = JZ4725B_IPU_RSZ_LUT_OUT_EN;
 173        unsigned int i;
 174
 175        weight = clamp_val(weight, 0, 512);
 176
 177        if (sharpness == 0)
 178                weight = weight >= 256 ? 512 : 0;
 179
 180        val |= (weight & JZ4725B_IPU_RSZ_LUT_COEF_MASK) << JZ4725B_IPU_RSZ_LUT_COEF_LSB;
 181        if (downscale || !!offset)
 182                val |= JZ4725B_IPU_RSZ_LUT_IN_EN;
 183
 184        regmap_write(ipu->map, reg, val);
 185
 186        if (downscale) {
 187                for (i = 1; i < offset; i++)
 188                        regmap_write(ipu->map, reg, JZ4725B_IPU_RSZ_LUT_IN_EN);
 189        }
 190}
 191
 192static void ingenic_ipu_set_downscale_coefs(struct ingenic_ipu *ipu,
 193                                            unsigned int reg,
 194                                            unsigned int num,
 195                                            unsigned int denom)
 196{
 197        unsigned int i, offset, weight, weight_num = denom;
 198
 199        for (i = 0; i < num; i++) {
 200                weight_num = num + (weight_num - num) % (num * 2);
 201                weight = 512 - 512 * (weight_num - num) / (num * 2);
 202                weight_num += denom * 2;
 203                offset = (weight_num - num) / (num * 2);
 204
 205                ipu->soc_info->set_coefs(ipu, reg, ipu->sharpness,
 206                                         true, weight, offset);
 207        }
 208}
 209
 210static void ingenic_ipu_set_integer_upscale_coefs(struct ingenic_ipu *ipu,
 211                                                  unsigned int reg,
 212                                                  unsigned int num)
 213{
 214        /*
 215         * Force nearest-neighbor scaling and use simple math when upscaling
 216         * by an integer ratio. It looks better, and fixes a few problem cases.
 217         */
 218        unsigned int i;
 219
 220        for (i = 0; i < num; i++)
 221                ipu->soc_info->set_coefs(ipu, reg, 0, false, 512, i == num - 1);
 222}
 223
 224static void ingenic_ipu_set_upscale_coefs(struct ingenic_ipu *ipu,
 225                                          unsigned int reg,
 226                                          unsigned int num,
 227                                          unsigned int denom)
 228{
 229        unsigned int i, offset, weight, weight_num = 0;
 230
 231        for (i = 0; i < num; i++) {
 232                weight = 512 - 512 * weight_num / num;
 233                weight_num += denom;
 234                offset = weight_num >= num;
 235
 236                if (offset)
 237                        weight_num -= num;
 238
 239                ipu->soc_info->set_coefs(ipu, reg, ipu->sharpness,
 240                                         false, weight, offset);
 241        }
 242}
 243
 244static void ingenic_ipu_set_coefs(struct ingenic_ipu *ipu, unsigned int reg,
 245                                  unsigned int num, unsigned int denom)
 246{
 247        /* Begin programming the LUT */
 248        regmap_write(ipu->map, reg, -1);
 249
 250        if (denom > num)
 251                ingenic_ipu_set_downscale_coefs(ipu, reg, num, denom);
 252        else if (denom == 1)
 253                ingenic_ipu_set_integer_upscale_coefs(ipu, reg, num);
 254        else
 255                ingenic_ipu_set_upscale_coefs(ipu, reg, num, denom);
 256}
 257
 258static int reduce_fraction(unsigned int *num, unsigned int *denom)
 259{
 260        unsigned long d = gcd(*num, *denom);
 261
 262        /* The scaling table has only 31 entries */
 263        if (*num > 31 * d)
 264                return -EINVAL;
 265
 266        *num /= d;
 267        *denom /= d;
 268        return 0;
 269}
 270
 271static inline bool osd_changed(struct drm_plane_state *state,
 272                               struct drm_plane_state *oldstate)
 273{
 274        return state->src_x != oldstate->src_x ||
 275                state->src_y != oldstate->src_y ||
 276                state->src_w != oldstate->src_w ||
 277                state->src_h != oldstate->src_h ||
 278                state->crtc_x != oldstate->crtc_x ||
 279                state->crtc_y != oldstate->crtc_y ||
 280                state->crtc_w != oldstate->crtc_w ||
 281                state->crtc_h != oldstate->crtc_h;
 282}
 283
 284static void ingenic_ipu_plane_atomic_update(struct drm_plane *plane,
 285                                            struct drm_plane_state *oldstate)
 286{
 287        struct ingenic_ipu *ipu = plane_to_ingenic_ipu(plane);
 288        struct drm_plane_state *state = plane->state;
 289        const struct drm_format_info *finfo;
 290        u32 ctrl, stride = 0, coef_index = 0, format = 0;
 291        bool needs_modeset, upscaling_w, upscaling_h;
 292        int err;
 293
 294        if (!state || !state->fb)
 295                return;
 296
 297        finfo = drm_format_info(state->fb->format->format);
 298
 299        if (!ipu->clk_enabled) {
 300                err = clk_enable(ipu->clk);
 301                if (err) {
 302                        dev_err(ipu->dev, "Unable to enable clock: %d\n", err);
 303                        return;
 304                }
 305
 306                ipu->clk_enabled = true;
 307        }
 308
 309        /* Reset all the registers if needed */
 310        needs_modeset = drm_atomic_crtc_needs_modeset(state->crtc->state);
 311        if (needs_modeset) {
 312                regmap_set_bits(ipu->map, JZ_REG_IPU_CTRL, JZ_IPU_CTRL_RST);
 313
 314                /* Enable the chip */
 315                regmap_set_bits(ipu->map, JZ_REG_IPU_CTRL,
 316                                JZ_IPU_CTRL_CHIP_EN | JZ_IPU_CTRL_LCDC_SEL);
 317        }
 318
 319        /* New addresses will be committed in vblank handler... */
 320        ipu->addr_y = drm_fb_cma_get_gem_addr(state->fb, state, 0);
 321        if (finfo->num_planes > 1)
 322                ipu->addr_u = drm_fb_cma_get_gem_addr(state->fb, state, 1);
 323        if (finfo->num_planes > 2)
 324                ipu->addr_v = drm_fb_cma_get_gem_addr(state->fb, state, 2);
 325
 326        if (!needs_modeset)
 327                return;
 328
 329        /* Or right here if we're doing a full modeset. */
 330        regmap_write(ipu->map, JZ_REG_IPU_Y_ADDR, ipu->addr_y);
 331        regmap_write(ipu->map, JZ_REG_IPU_U_ADDR, ipu->addr_u);
 332        regmap_write(ipu->map, JZ_REG_IPU_V_ADDR, ipu->addr_v);
 333
 334        if (finfo->num_planes == 1)
 335                regmap_set_bits(ipu->map, JZ_REG_IPU_CTRL, JZ_IPU_CTRL_SPKG_SEL);
 336
 337        ingenic_drm_plane_config(ipu->master, plane, DRM_FORMAT_XRGB8888);
 338
 339        /* Set the input height/width/strides */
 340        if (finfo->num_planes > 2)
 341                stride = ((state->src_w >> 16) * finfo->cpp[2] / finfo->hsub)
 342                        << JZ_IPU_UV_STRIDE_V_LSB;
 343
 344        if (finfo->num_planes > 1)
 345                stride |= ((state->src_w >> 16) * finfo->cpp[1] / finfo->hsub)
 346                        << JZ_IPU_UV_STRIDE_U_LSB;
 347
 348        regmap_write(ipu->map, JZ_REG_IPU_UV_STRIDE, stride);
 349
 350        stride = ((state->src_w >> 16) * finfo->cpp[0]) << JZ_IPU_Y_STRIDE_Y_LSB;
 351        regmap_write(ipu->map, JZ_REG_IPU_Y_STRIDE, stride);
 352
 353        regmap_write(ipu->map, JZ_REG_IPU_IN_GS,
 354                     (stride << JZ_IPU_IN_GS_W_LSB) |
 355                     ((state->src_h >> 16) << JZ_IPU_IN_GS_H_LSB));
 356
 357        switch (finfo->format) {
 358        case DRM_FORMAT_XRGB1555:
 359                format = JZ_IPU_D_FMT_IN_FMT_RGB555 |
 360                        JZ_IPU_D_FMT_RGB_OUT_OFT_RGB;
 361                break;
 362        case DRM_FORMAT_XBGR1555:
 363                format = JZ_IPU_D_FMT_IN_FMT_RGB555 |
 364                        JZ_IPU_D_FMT_RGB_OUT_OFT_BGR;
 365                break;
 366        case DRM_FORMAT_RGB565:
 367                format = JZ_IPU_D_FMT_IN_FMT_RGB565 |
 368                        JZ_IPU_D_FMT_RGB_OUT_OFT_RGB;
 369                break;
 370        case DRM_FORMAT_BGR565:
 371                format = JZ_IPU_D_FMT_IN_FMT_RGB565 |
 372                        JZ_IPU_D_FMT_RGB_OUT_OFT_BGR;
 373                break;
 374        case DRM_FORMAT_XRGB8888:
 375        case DRM_FORMAT_XYUV8888:
 376                format = JZ_IPU_D_FMT_IN_FMT_RGB888 |
 377                        JZ_IPU_D_FMT_RGB_OUT_OFT_RGB;
 378                break;
 379        case DRM_FORMAT_XBGR8888:
 380                format = JZ_IPU_D_FMT_IN_FMT_RGB888 |
 381                        JZ_IPU_D_FMT_RGB_OUT_OFT_BGR;
 382                break;
 383        case DRM_FORMAT_YUYV:
 384                format = JZ_IPU_D_FMT_IN_FMT_YUV422 |
 385                        JZ_IPU_D_FMT_YUV_VY1UY0;
 386                break;
 387        case DRM_FORMAT_YVYU:
 388                format = JZ_IPU_D_FMT_IN_FMT_YUV422 |
 389                        JZ_IPU_D_FMT_YUV_UY1VY0;
 390                break;
 391        case DRM_FORMAT_UYVY:
 392                format = JZ_IPU_D_FMT_IN_FMT_YUV422 |
 393                        JZ_IPU_D_FMT_YUV_Y1VY0U;
 394                break;
 395        case DRM_FORMAT_VYUY:
 396                format = JZ_IPU_D_FMT_IN_FMT_YUV422 |
 397                        JZ_IPU_D_FMT_YUV_Y1UY0V;
 398                break;
 399        case DRM_FORMAT_YUV411:
 400                format = JZ_IPU_D_FMT_IN_FMT_YUV411;
 401                break;
 402        case DRM_FORMAT_YUV420:
 403                format = JZ_IPU_D_FMT_IN_FMT_YUV420;
 404                break;
 405        case DRM_FORMAT_YUV422:
 406                format = JZ_IPU_D_FMT_IN_FMT_YUV422;
 407                break;
 408        case DRM_FORMAT_YUV444:
 409                format = JZ_IPU_D_FMT_IN_FMT_YUV444;
 410                break;
 411        default:
 412                WARN_ONCE(1, "Unsupported format");
 413                break;
 414        }
 415
 416        /* Fix output to RGB888 */
 417        format |= JZ_IPU_D_FMT_OUT_FMT_RGB888;
 418
 419        /* Set pixel format */
 420        regmap_write(ipu->map, JZ_REG_IPU_D_FMT, format);
 421
 422        /* Set the output height/width/stride */
 423        regmap_write(ipu->map, JZ_REG_IPU_OUT_GS,
 424                     ((state->crtc_w * 4) << JZ_IPU_OUT_GS_W_LSB)
 425                     | state->crtc_h << JZ_IPU_OUT_GS_H_LSB);
 426        regmap_write(ipu->map, JZ_REG_IPU_OUT_STRIDE, state->crtc_w * 4);
 427
 428        if (finfo->is_yuv) {
 429                regmap_set_bits(ipu->map, JZ_REG_IPU_CTRL, JZ_IPU_CTRL_CSC_EN);
 430
 431                /*
 432                 * Offsets for Chroma/Luma.
 433                 * y = source Y - LUMA,
 434                 * u = source Cb - CHROMA,
 435                 * v = source Cr - CHROMA
 436                 */
 437                regmap_write(ipu->map, JZ_REG_IPU_CSC_OFFSET,
 438                             128 << JZ_IPU_CSC_OFFSET_CHROMA_LSB |
 439                             0 << JZ_IPU_CSC_OFFSET_LUMA_LSB);
 440
 441                /*
 442                 * YUV422 to RGB conversion table.
 443                 * R = C0 / 0x400 * y + C1 / 0x400 * v
 444                 * G = C0 / 0x400 * y - C2 / 0x400 * u - C3 / 0x400 * v
 445                 * B = C0 / 0x400 * y + C4 / 0x400 * u
 446                 */
 447                regmap_write(ipu->map, JZ_REG_IPU_CSC_C0_COEF, 0x4a8);
 448                regmap_write(ipu->map, JZ_REG_IPU_CSC_C1_COEF, 0x662);
 449                regmap_write(ipu->map, JZ_REG_IPU_CSC_C2_COEF, 0x191);
 450                regmap_write(ipu->map, JZ_REG_IPU_CSC_C3_COEF, 0x341);
 451                regmap_write(ipu->map, JZ_REG_IPU_CSC_C4_COEF, 0x811);
 452        }
 453
 454        ctrl = 0;
 455
 456        /*
 457         * Must set ZOOM_SEL before programming bicubic LUTs.
 458         * If the IPU supports bicubic, we enable it unconditionally, since it
 459         * can do anything bilinear can and more.
 460         */
 461        if (ipu->soc_info->has_bicubic)
 462                ctrl |= JZ_IPU_CTRL_ZOOM_SEL;
 463
 464        upscaling_w = ipu->num_w > ipu->denom_w;
 465        if (upscaling_w)
 466                ctrl |= JZ_IPU_CTRL_HSCALE;
 467
 468        if (ipu->num_w != 1 || ipu->denom_w != 1) {
 469                if (!ipu->soc_info->has_bicubic && !upscaling_w)
 470                        coef_index |= (ipu->denom_w - 1) << 16;
 471                else
 472                        coef_index |= (ipu->num_w - 1) << 16;
 473                ctrl |= JZ_IPU_CTRL_HRSZ_EN;
 474        }
 475
 476        upscaling_h = ipu->num_h > ipu->denom_h;
 477        if (upscaling_h)
 478                ctrl |= JZ_IPU_CTRL_VSCALE;
 479
 480        if (ipu->num_h != 1 || ipu->denom_h != 1) {
 481                if (!ipu->soc_info->has_bicubic && !upscaling_h)
 482                        coef_index |= ipu->denom_h - 1;
 483                else
 484                        coef_index |= ipu->num_h - 1;
 485                ctrl |= JZ_IPU_CTRL_VRSZ_EN;
 486        }
 487
 488        regmap_update_bits(ipu->map, JZ_REG_IPU_CTRL, JZ_IPU_CTRL_ZOOM_SEL |
 489                           JZ_IPU_CTRL_HRSZ_EN | JZ_IPU_CTRL_VRSZ_EN |
 490                           JZ_IPU_CTRL_HSCALE | JZ_IPU_CTRL_VSCALE, ctrl);
 491
 492        /* Set the LUT index register */
 493        regmap_write(ipu->map, JZ_REG_IPU_RSZ_COEF_INDEX, coef_index);
 494
 495        if (ipu->num_w != 1 || ipu->denom_w != 1)
 496                ingenic_ipu_set_coefs(ipu, JZ_REG_IPU_HRSZ_COEF_LUT,
 497                                      ipu->num_w, ipu->denom_w);
 498
 499        if (ipu->num_h != 1 || ipu->denom_h != 1)
 500                ingenic_ipu_set_coefs(ipu, JZ_REG_IPU_VRSZ_COEF_LUT,
 501                                      ipu->num_h, ipu->denom_h);
 502
 503        /* Clear STATUS register */
 504        regmap_write(ipu->map, JZ_REG_IPU_STATUS, 0);
 505
 506        /* Start IPU */
 507        regmap_set_bits(ipu->map, JZ_REG_IPU_CTRL,
 508                        JZ_IPU_CTRL_RUN | JZ_IPU_CTRL_FM_IRQ_EN);
 509
 510        dev_dbg(ipu->dev, "Scaling %ux%u to %ux%u (%u:%u horiz, %u:%u vert)\n",
 511                state->src_w >> 16, state->src_h >> 16,
 512                state->crtc_w, state->crtc_h,
 513                ipu->num_w, ipu->denom_w, ipu->num_h, ipu->denom_h);
 514}
 515
 516static int ingenic_ipu_plane_atomic_check(struct drm_plane *plane,
 517                                          struct drm_plane_state *state)
 518{
 519        unsigned int num_w, denom_w, num_h, denom_h, xres, yres, max_w, max_h;
 520        struct ingenic_ipu *ipu = plane_to_ingenic_ipu(plane);
 521        struct drm_crtc *crtc = state->crtc ?: plane->state->crtc;
 522        struct drm_crtc_state *crtc_state;
 523
 524        if (!crtc)
 525                return 0;
 526
 527        crtc_state = drm_atomic_get_existing_crtc_state(state->state, crtc);
 528        if (WARN_ON(!crtc_state))
 529                return -EINVAL;
 530
 531        /* Request a full modeset if we are enabling or disabling the IPU. */
 532        if (!plane->state->crtc ^ !state->crtc)
 533                crtc_state->mode_changed = true;
 534
 535        if (!state->crtc ||
 536            !crtc_state->mode.hdisplay || !crtc_state->mode.vdisplay)
 537                return 0;
 538
 539        /* Plane must be fully visible */
 540        if (state->crtc_x < 0 || state->crtc_y < 0 ||
 541            state->crtc_x + state->crtc_w > crtc_state->mode.hdisplay ||
 542            state->crtc_y + state->crtc_h > crtc_state->mode.vdisplay)
 543                return -EINVAL;
 544
 545        /* Minimum size is 4x4 */
 546        if ((state->src_w >> 16) < 4 || (state->src_h >> 16) < 4)
 547                return -EINVAL;
 548
 549        /* Input and output lines must have an even number of pixels. */
 550        if (((state->src_w >> 16) & 1) || (state->crtc_w & 1))
 551                return -EINVAL;
 552
 553        if (!osd_changed(state, plane->state))
 554                return 0;
 555
 556        crtc_state->mode_changed = true;
 557
 558        xres = state->src_w >> 16;
 559        yres = state->src_h >> 16;
 560
 561        /*
 562         * Increase the scaled image's theorical width/height until we find a
 563         * configuration that has valid scaling coefficients, up to 102% of the
 564         * screen's resolution. This makes sure that we can scale from almost
 565         * every resolution possible at the cost of a very small distorsion.
 566         * The CRTC_W / CRTC_H are not modified.
 567         */
 568        max_w = crtc_state->mode.hdisplay * 102 / 100;
 569        max_h = crtc_state->mode.vdisplay * 102 / 100;
 570
 571        for (denom_w = xres, num_w = state->crtc_w; num_w <= max_w; num_w++)
 572                if (!reduce_fraction(&num_w, &denom_w))
 573                        break;
 574        if (num_w > max_w)
 575                return -EINVAL;
 576
 577        for (denom_h = yres, num_h = state->crtc_h; num_h <= max_h; num_h++)
 578                if (!reduce_fraction(&num_h, &denom_h))
 579                        break;
 580        if (num_h > max_h)
 581                return -EINVAL;
 582
 583        ipu->num_w = num_w;
 584        ipu->num_h = num_h;
 585        ipu->denom_w = denom_w;
 586        ipu->denom_h = denom_h;
 587
 588        return 0;
 589}
 590
 591static void ingenic_ipu_plane_atomic_disable(struct drm_plane *plane,
 592                                             struct drm_plane_state *old_state)
 593{
 594        struct ingenic_ipu *ipu = plane_to_ingenic_ipu(plane);
 595
 596        regmap_set_bits(ipu->map, JZ_REG_IPU_CTRL, JZ_IPU_CTRL_STOP);
 597        regmap_clear_bits(ipu->map, JZ_REG_IPU_CTRL, JZ_IPU_CTRL_CHIP_EN);
 598
 599        ingenic_drm_plane_disable(ipu->master, plane);
 600
 601        if (ipu->clk_enabled) {
 602                clk_disable(ipu->clk);
 603                ipu->clk_enabled = false;
 604        }
 605}
 606
 607static const struct drm_plane_helper_funcs ingenic_ipu_plane_helper_funcs = {
 608        .atomic_update          = ingenic_ipu_plane_atomic_update,
 609        .atomic_check           = ingenic_ipu_plane_atomic_check,
 610        .atomic_disable         = ingenic_ipu_plane_atomic_disable,
 611        .prepare_fb             = drm_gem_fb_prepare_fb,
 612};
 613
 614static int
 615ingenic_ipu_plane_atomic_get_property(struct drm_plane *plane,
 616                                      const struct drm_plane_state *state,
 617                                      struct drm_property *property, u64 *val)
 618{
 619        struct ingenic_ipu *ipu = plane_to_ingenic_ipu(plane);
 620
 621        if (property != ipu->sharpness_prop)
 622                return -EINVAL;
 623
 624        *val = ipu->sharpness;
 625
 626        return 0;
 627}
 628
 629static int
 630ingenic_ipu_plane_atomic_set_property(struct drm_plane *plane,
 631                                      struct drm_plane_state *state,
 632                                      struct drm_property *property, u64 val)
 633{
 634        struct ingenic_ipu *ipu = plane_to_ingenic_ipu(plane);
 635        struct drm_crtc_state *crtc_state;
 636
 637        if (property != ipu->sharpness_prop)
 638                return -EINVAL;
 639
 640        ipu->sharpness = val;
 641
 642        if (state->crtc) {
 643                crtc_state = drm_atomic_get_existing_crtc_state(state->state, state->crtc);
 644                if (WARN_ON(!crtc_state))
 645                        return -EINVAL;
 646
 647                crtc_state->mode_changed = true;
 648        }
 649
 650        return 0;
 651}
 652
 653static const struct drm_plane_funcs ingenic_ipu_plane_funcs = {
 654        .update_plane           = drm_atomic_helper_update_plane,
 655        .disable_plane          = drm_atomic_helper_disable_plane,
 656        .reset                  = drm_atomic_helper_plane_reset,
 657        .destroy                = drm_plane_cleanup,
 658
 659        .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
 660        .atomic_destroy_state   = drm_atomic_helper_plane_destroy_state,
 661
 662        .atomic_get_property    = ingenic_ipu_plane_atomic_get_property,
 663        .atomic_set_property    = ingenic_ipu_plane_atomic_set_property,
 664};
 665
 666static irqreturn_t ingenic_ipu_irq_handler(int irq, void *arg)
 667{
 668        struct ingenic_ipu *ipu = arg;
 669        struct drm_crtc *crtc = drm_crtc_from_index(ipu->drm, 0);
 670        unsigned int dummy;
 671
 672        /* dummy read allows CPU to reconfigure IPU */
 673        if (ipu->soc_info->manual_restart)
 674                regmap_read(ipu->map, JZ_REG_IPU_STATUS, &dummy);
 675
 676        /* ACK interrupt */
 677        regmap_write(ipu->map, JZ_REG_IPU_STATUS, 0);
 678
 679        /* Set previously cached addresses */
 680        regmap_write(ipu->map, JZ_REG_IPU_Y_ADDR, ipu->addr_y);
 681        regmap_write(ipu->map, JZ_REG_IPU_U_ADDR, ipu->addr_u);
 682        regmap_write(ipu->map, JZ_REG_IPU_V_ADDR, ipu->addr_v);
 683
 684        /* Run IPU for the new frame */
 685        if (ipu->soc_info->manual_restart)
 686                regmap_set_bits(ipu->map, JZ_REG_IPU_CTRL, JZ_IPU_CTRL_RUN);
 687
 688        drm_crtc_handle_vblank(crtc);
 689
 690        return IRQ_HANDLED;
 691}
 692
 693static const struct regmap_config ingenic_ipu_regmap_config = {
 694        .reg_bits = 32,
 695        .val_bits = 32,
 696        .reg_stride = 4,
 697
 698        .max_register = JZ_REG_IPU_OUT_PHY_T_ADDR,
 699};
 700
 701static int ingenic_ipu_bind(struct device *dev, struct device *master, void *d)
 702{
 703        struct platform_device *pdev = to_platform_device(dev);
 704        const struct soc_info *soc_info;
 705        struct drm_device *drm = d;
 706        struct drm_plane *plane;
 707        struct ingenic_ipu *ipu;
 708        void __iomem *base;
 709        unsigned int sharpness_max;
 710        int err, irq;
 711
 712        ipu = devm_kzalloc(dev, sizeof(*ipu), GFP_KERNEL);
 713        if (!ipu)
 714                return -ENOMEM;
 715
 716        soc_info = of_device_get_match_data(dev);
 717        if (!soc_info) {
 718                dev_err(dev, "Missing platform data\n");
 719                return -EINVAL;
 720        }
 721
 722        ipu->dev = dev;
 723        ipu->drm = drm;
 724        ipu->master = master;
 725        ipu->soc_info = soc_info;
 726
 727        base = devm_platform_ioremap_resource(pdev, 0);
 728        if (IS_ERR(base)) {
 729                dev_err(dev, "Failed to get memory resource\n");
 730                return PTR_ERR(base);
 731        }
 732
 733        ipu->map = devm_regmap_init_mmio(dev, base, &ingenic_ipu_regmap_config);
 734        if (IS_ERR(ipu->map)) {
 735                dev_err(dev, "Failed to create regmap\n");
 736                return PTR_ERR(ipu->map);
 737        }
 738
 739        irq = platform_get_irq(pdev, 0);
 740        if (irq < 0)
 741                return irq;
 742
 743        ipu->clk = devm_clk_get(dev, "ipu");
 744        if (IS_ERR(ipu->clk)) {
 745                dev_err(dev, "Failed to get pixel clock\n");
 746                return PTR_ERR(ipu->clk);
 747        }
 748
 749        err = devm_request_irq(dev, irq, ingenic_ipu_irq_handler, 0,
 750                               dev_name(dev), ipu);
 751        if (err) {
 752                dev_err(dev, "Unable to request IRQ\n");
 753                return err;
 754        }
 755
 756        plane = &ipu->plane;
 757        dev_set_drvdata(dev, plane);
 758
 759        drm_plane_helper_add(plane, &ingenic_ipu_plane_helper_funcs);
 760
 761        err = drm_universal_plane_init(drm, plane, 1, &ingenic_ipu_plane_funcs,
 762                                       soc_info->formats, soc_info->num_formats,
 763                                       NULL, DRM_PLANE_TYPE_PRIMARY, NULL);
 764        if (err) {
 765                dev_err(dev, "Failed to init plane: %i\n", err);
 766                return err;
 767        }
 768
 769        /*
 770         * Sharpness settings range is [0,32]
 771         * 0       : nearest-neighbor
 772         * 1       : bilinear
 773         * 2 .. 32 : bicubic (translated to sharpness factor -0.25 .. -4.0)
 774         */
 775        sharpness_max = soc_info->has_bicubic ? 32 : 1;
 776        ipu->sharpness_prop = drm_property_create_range(drm, 0, "sharpness",
 777                                                        0, sharpness_max);
 778        if (!ipu->sharpness_prop) {
 779                dev_err(dev, "Unable to create sharpness property\n");
 780                return -ENOMEM;
 781        }
 782
 783        /* Default sharpness factor: -0.125 * 8 = -1.0 */
 784        ipu->sharpness = soc_info->has_bicubic ? 8 : 1;
 785        drm_object_attach_property(&plane->base, ipu->sharpness_prop,
 786                                   ipu->sharpness);
 787
 788        err = clk_prepare(ipu->clk);
 789        if (err) {
 790                dev_err(dev, "Unable to prepare clock\n");
 791                return err;
 792        }
 793
 794        return 0;
 795}
 796
 797static void ingenic_ipu_unbind(struct device *dev,
 798                               struct device *master, void *d)
 799{
 800        struct ingenic_ipu *ipu = dev_get_drvdata(dev);
 801
 802        clk_unprepare(ipu->clk);
 803}
 804
 805static const struct component_ops ingenic_ipu_ops = {
 806        .bind = ingenic_ipu_bind,
 807        .unbind = ingenic_ipu_unbind,
 808};
 809
 810static int ingenic_ipu_probe(struct platform_device *pdev)
 811{
 812        return component_add(&pdev->dev, &ingenic_ipu_ops);
 813}
 814
 815static int ingenic_ipu_remove(struct platform_device *pdev)
 816{
 817        component_del(&pdev->dev, &ingenic_ipu_ops);
 818        return 0;
 819}
 820
 821static const u32 jz4725b_ipu_formats[] = {
 822        /*
 823         * While officially supported, packed YUV 4:2:2 formats can cause
 824         * random hardware crashes on JZ4725B under certain circumstances.
 825         * It seems to happen with some specific resize ratios.
 826         * Until a proper workaround or fix is found, disable these formats.
 827        DRM_FORMAT_YUYV,
 828        DRM_FORMAT_YVYU,
 829        DRM_FORMAT_UYVY,
 830        DRM_FORMAT_VYUY,
 831        */
 832        DRM_FORMAT_YUV411,
 833        DRM_FORMAT_YUV420,
 834        DRM_FORMAT_YUV422,
 835        DRM_FORMAT_YUV444,
 836};
 837
 838static const struct soc_info jz4725b_soc_info = {
 839        .formats        = jz4725b_ipu_formats,
 840        .num_formats    = ARRAY_SIZE(jz4725b_ipu_formats),
 841        .has_bicubic    = false,
 842        .manual_restart = true,
 843        .set_coefs      = jz4725b_set_coefs,
 844};
 845
 846static const u32 jz4760_ipu_formats[] = {
 847        DRM_FORMAT_XRGB1555,
 848        DRM_FORMAT_XBGR1555,
 849        DRM_FORMAT_RGB565,
 850        DRM_FORMAT_BGR565,
 851        DRM_FORMAT_XRGB8888,
 852        DRM_FORMAT_XBGR8888,
 853        DRM_FORMAT_YUYV,
 854        DRM_FORMAT_YVYU,
 855        DRM_FORMAT_UYVY,
 856        DRM_FORMAT_VYUY,
 857        DRM_FORMAT_YUV411,
 858        DRM_FORMAT_YUV420,
 859        DRM_FORMAT_YUV422,
 860        DRM_FORMAT_YUV444,
 861        DRM_FORMAT_XYUV8888,
 862};
 863
 864static const struct soc_info jz4760_soc_info = {
 865        .formats        = jz4760_ipu_formats,
 866        .num_formats    = ARRAY_SIZE(jz4760_ipu_formats),
 867        .has_bicubic    = true,
 868        .manual_restart = false,
 869        .set_coefs      = jz4760_set_coefs,
 870};
 871
 872static const struct of_device_id ingenic_ipu_of_match[] = {
 873        { .compatible = "ingenic,jz4725b-ipu", .data = &jz4725b_soc_info },
 874        { .compatible = "ingenic,jz4760-ipu", .data = &jz4760_soc_info },
 875        { /* sentinel */ },
 876};
 877MODULE_DEVICE_TABLE(of, ingenic_ipu_of_match);
 878
 879static struct platform_driver ingenic_ipu_driver = {
 880        .driver = {
 881                .name = "ingenic-ipu",
 882                .of_match_table = ingenic_ipu_of_match,
 883        },
 884        .probe = ingenic_ipu_probe,
 885        .remove = ingenic_ipu_remove,
 886};
 887
 888struct platform_driver *ingenic_ipu_driver_ptr = &ingenic_ipu_driver;
 889