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