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_atomic_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 32 struct ingenic_ipu; 33 34 struct 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 45 struct 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 68 static 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 */ 89 static 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 */ 116 static 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 168 static 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 192 static 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 210 static 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 224 static 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 244 static 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 258 static 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 271 static 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 284 static void ingenic_ipu_plane_atomic_update(struct drm_plane *plane, 285 struct drm_atomic_state *state) 286 { 287 struct ingenic_ipu *ipu = plane_to_ingenic_ipu(plane); 288 struct drm_plane_state *newstate = drm_atomic_get_new_plane_state(state, 289 plane); 290 const struct drm_format_info *finfo; 291 u32 ctrl, stride = 0, coef_index = 0, format = 0; 292 bool needs_modeset, upscaling_w, upscaling_h; 293 int err; 294 295 if (!newstate || !newstate->fb) 296 return; 297 298 finfo = drm_format_info(newstate->fb->format->format); 299 300 if (!ipu->clk_enabled) { 301 err = clk_enable(ipu->clk); 302 if (err) { 303 dev_err(ipu->dev, "Unable to enable clock: %d\n", err); 304 return; 305 } 306 307 ipu->clk_enabled = true; 308 } 309 310 /* Reset all the registers if needed */ 311 needs_modeset = drm_atomic_crtc_needs_modeset(newstate->crtc->state); 312 if (needs_modeset) { 313 regmap_set_bits(ipu->map, JZ_REG_IPU_CTRL, JZ_IPU_CTRL_RST); 314 315 /* Enable the chip */ 316 regmap_set_bits(ipu->map, JZ_REG_IPU_CTRL, 317 JZ_IPU_CTRL_CHIP_EN | JZ_IPU_CTRL_LCDC_SEL); 318 } 319 320 /* New addresses will be committed in vblank handler... */ 321 ipu->addr_y = drm_fb_cma_get_gem_addr(newstate->fb, newstate, 0); 322 if (finfo->num_planes > 1) 323 ipu->addr_u = drm_fb_cma_get_gem_addr(newstate->fb, newstate, 324 1); 325 if (finfo->num_planes > 2) 326 ipu->addr_v = drm_fb_cma_get_gem_addr(newstate->fb, newstate, 327 2); 328 329 if (!needs_modeset) 330 return; 331 332 /* Or right here if we're doing a full modeset. */ 333 regmap_write(ipu->map, JZ_REG_IPU_Y_ADDR, ipu->addr_y); 334 regmap_write(ipu->map, JZ_REG_IPU_U_ADDR, ipu->addr_u); 335 regmap_write(ipu->map, JZ_REG_IPU_V_ADDR, ipu->addr_v); 336 337 if (finfo->num_planes == 1) 338 regmap_set_bits(ipu->map, JZ_REG_IPU_CTRL, JZ_IPU_CTRL_SPKG_SEL); 339 340 ingenic_drm_plane_config(ipu->master, plane, DRM_FORMAT_XRGB8888); 341 342 /* Set the input height/width/strides */ 343 if (finfo->num_planes > 2) 344 stride = ((newstate->src_w >> 16) * finfo->cpp[2] / finfo->hsub) 345 << JZ_IPU_UV_STRIDE_V_LSB; 346 347 if (finfo->num_planes > 1) 348 stride |= ((newstate->src_w >> 16) * finfo->cpp[1] / finfo->hsub) 349 << JZ_IPU_UV_STRIDE_U_LSB; 350 351 regmap_write(ipu->map, JZ_REG_IPU_UV_STRIDE, stride); 352 353 stride = ((newstate->src_w >> 16) * finfo->cpp[0]) << JZ_IPU_Y_STRIDE_Y_LSB; 354 regmap_write(ipu->map, JZ_REG_IPU_Y_STRIDE, stride); 355 356 regmap_write(ipu->map, JZ_REG_IPU_IN_GS, 357 (stride << JZ_IPU_IN_GS_W_LSB) | 358 ((newstate->src_h >> 16) << JZ_IPU_IN_GS_H_LSB)); 359 360 switch (finfo->format) { 361 case DRM_FORMAT_XRGB1555: 362 format = JZ_IPU_D_FMT_IN_FMT_RGB555 | 363 JZ_IPU_D_FMT_RGB_OUT_OFT_RGB; 364 break; 365 case DRM_FORMAT_XBGR1555: 366 format = JZ_IPU_D_FMT_IN_FMT_RGB555 | 367 JZ_IPU_D_FMT_RGB_OUT_OFT_BGR; 368 break; 369 case DRM_FORMAT_RGB565: 370 format = JZ_IPU_D_FMT_IN_FMT_RGB565 | 371 JZ_IPU_D_FMT_RGB_OUT_OFT_RGB; 372 break; 373 case DRM_FORMAT_BGR565: 374 format = JZ_IPU_D_FMT_IN_FMT_RGB565 | 375 JZ_IPU_D_FMT_RGB_OUT_OFT_BGR; 376 break; 377 case DRM_FORMAT_XRGB8888: 378 case DRM_FORMAT_XYUV8888: 379 format = JZ_IPU_D_FMT_IN_FMT_RGB888 | 380 JZ_IPU_D_FMT_RGB_OUT_OFT_RGB; 381 break; 382 case DRM_FORMAT_XBGR8888: 383 format = JZ_IPU_D_FMT_IN_FMT_RGB888 | 384 JZ_IPU_D_FMT_RGB_OUT_OFT_BGR; 385 break; 386 case DRM_FORMAT_YUYV: 387 format = JZ_IPU_D_FMT_IN_FMT_YUV422 | 388 JZ_IPU_D_FMT_YUV_VY1UY0; 389 break; 390 case DRM_FORMAT_YVYU: 391 format = JZ_IPU_D_FMT_IN_FMT_YUV422 | 392 JZ_IPU_D_FMT_YUV_UY1VY0; 393 break; 394 case DRM_FORMAT_UYVY: 395 format = JZ_IPU_D_FMT_IN_FMT_YUV422 | 396 JZ_IPU_D_FMT_YUV_Y1VY0U; 397 break; 398 case DRM_FORMAT_VYUY: 399 format = JZ_IPU_D_FMT_IN_FMT_YUV422 | 400 JZ_IPU_D_FMT_YUV_Y1UY0V; 401 break; 402 case DRM_FORMAT_YUV411: 403 format = JZ_IPU_D_FMT_IN_FMT_YUV411; 404 break; 405 case DRM_FORMAT_YUV420: 406 format = JZ_IPU_D_FMT_IN_FMT_YUV420; 407 break; 408 case DRM_FORMAT_YUV422: 409 format = JZ_IPU_D_FMT_IN_FMT_YUV422; 410 break; 411 case DRM_FORMAT_YUV444: 412 format = JZ_IPU_D_FMT_IN_FMT_YUV444; 413 break; 414 default: 415 WARN_ONCE(1, "Unsupported format"); 416 break; 417 } 418 419 /* Fix output to RGB888 */ 420 format |= JZ_IPU_D_FMT_OUT_FMT_RGB888; 421 422 /* Set pixel format */ 423 regmap_write(ipu->map, JZ_REG_IPU_D_FMT, format); 424 425 /* Set the output height/width/stride */ 426 regmap_write(ipu->map, JZ_REG_IPU_OUT_GS, 427 ((newstate->crtc_w * 4) << JZ_IPU_OUT_GS_W_LSB) 428 | newstate->crtc_h << JZ_IPU_OUT_GS_H_LSB); 429 regmap_write(ipu->map, JZ_REG_IPU_OUT_STRIDE, newstate->crtc_w * 4); 430 431 if (finfo->is_yuv) { 432 regmap_set_bits(ipu->map, JZ_REG_IPU_CTRL, JZ_IPU_CTRL_CSC_EN); 433 434 /* 435 * Offsets for Chroma/Luma. 436 * y = source Y - LUMA, 437 * u = source Cb - CHROMA, 438 * v = source Cr - CHROMA 439 */ 440 regmap_write(ipu->map, JZ_REG_IPU_CSC_OFFSET, 441 128 << JZ_IPU_CSC_OFFSET_CHROMA_LSB | 442 0 << JZ_IPU_CSC_OFFSET_LUMA_LSB); 443 444 /* 445 * YUV422 to RGB conversion table. 446 * R = C0 / 0x400 * y + C1 / 0x400 * v 447 * G = C0 / 0x400 * y - C2 / 0x400 * u - C3 / 0x400 * v 448 * B = C0 / 0x400 * y + C4 / 0x400 * u 449 */ 450 regmap_write(ipu->map, JZ_REG_IPU_CSC_C0_COEF, 0x4a8); 451 regmap_write(ipu->map, JZ_REG_IPU_CSC_C1_COEF, 0x662); 452 regmap_write(ipu->map, JZ_REG_IPU_CSC_C2_COEF, 0x191); 453 regmap_write(ipu->map, JZ_REG_IPU_CSC_C3_COEF, 0x341); 454 regmap_write(ipu->map, JZ_REG_IPU_CSC_C4_COEF, 0x811); 455 } 456 457 ctrl = 0; 458 459 /* 460 * Must set ZOOM_SEL before programming bicubic LUTs. 461 * If the IPU supports bicubic, we enable it unconditionally, since it 462 * can do anything bilinear can and more. 463 */ 464 if (ipu->soc_info->has_bicubic) 465 ctrl |= JZ_IPU_CTRL_ZOOM_SEL; 466 467 upscaling_w = ipu->num_w > ipu->denom_w; 468 if (upscaling_w) 469 ctrl |= JZ_IPU_CTRL_HSCALE; 470 471 if (ipu->num_w != 1 || ipu->denom_w != 1) { 472 if (!ipu->soc_info->has_bicubic && !upscaling_w) 473 coef_index |= (ipu->denom_w - 1) << 16; 474 else 475 coef_index |= (ipu->num_w - 1) << 16; 476 ctrl |= JZ_IPU_CTRL_HRSZ_EN; 477 } 478 479 upscaling_h = ipu->num_h > ipu->denom_h; 480 if (upscaling_h) 481 ctrl |= JZ_IPU_CTRL_VSCALE; 482 483 if (ipu->num_h != 1 || ipu->denom_h != 1) { 484 if (!ipu->soc_info->has_bicubic && !upscaling_h) 485 coef_index |= ipu->denom_h - 1; 486 else 487 coef_index |= ipu->num_h - 1; 488 ctrl |= JZ_IPU_CTRL_VRSZ_EN; 489 } 490 491 regmap_update_bits(ipu->map, JZ_REG_IPU_CTRL, JZ_IPU_CTRL_ZOOM_SEL | 492 JZ_IPU_CTRL_HRSZ_EN | JZ_IPU_CTRL_VRSZ_EN | 493 JZ_IPU_CTRL_HSCALE | JZ_IPU_CTRL_VSCALE, ctrl); 494 495 /* Set the LUT index register */ 496 regmap_write(ipu->map, JZ_REG_IPU_RSZ_COEF_INDEX, coef_index); 497 498 if (ipu->num_w != 1 || ipu->denom_w != 1) 499 ingenic_ipu_set_coefs(ipu, JZ_REG_IPU_HRSZ_COEF_LUT, 500 ipu->num_w, ipu->denom_w); 501 502 if (ipu->num_h != 1 || ipu->denom_h != 1) 503 ingenic_ipu_set_coefs(ipu, JZ_REG_IPU_VRSZ_COEF_LUT, 504 ipu->num_h, ipu->denom_h); 505 506 /* Clear STATUS register */ 507 regmap_write(ipu->map, JZ_REG_IPU_STATUS, 0); 508 509 /* Start IPU */ 510 regmap_set_bits(ipu->map, JZ_REG_IPU_CTRL, 511 JZ_IPU_CTRL_RUN | JZ_IPU_CTRL_FM_IRQ_EN); 512 513 dev_dbg(ipu->dev, "Scaling %ux%u to %ux%u (%u:%u horiz, %u:%u vert)\n", 514 newstate->src_w >> 16, newstate->src_h >> 16, 515 newstate->crtc_w, newstate->crtc_h, 516 ipu->num_w, ipu->denom_w, ipu->num_h, ipu->denom_h); 517 } 518 519 static int ingenic_ipu_plane_atomic_check(struct drm_plane *plane, 520 struct drm_atomic_state *state) 521 { 522 struct drm_plane_state *old_plane_state = drm_atomic_get_old_plane_state(state, 523 plane); 524 struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state, 525 plane); 526 unsigned int num_w, denom_w, num_h, denom_h, xres, yres, max_w, max_h; 527 struct ingenic_ipu *ipu = plane_to_ingenic_ipu(plane); 528 struct drm_crtc *crtc = new_plane_state->crtc ?: old_plane_state->crtc; 529 struct drm_crtc_state *crtc_state; 530 531 if (!crtc) 532 return 0; 533 534 crtc_state = drm_atomic_get_existing_crtc_state(state, crtc); 535 if (WARN_ON(!crtc_state)) 536 return -EINVAL; 537 538 /* Request a full modeset if we are enabling or disabling the IPU. */ 539 if (!old_plane_state->crtc ^ !new_plane_state->crtc) 540 crtc_state->mode_changed = true; 541 542 if (!new_plane_state->crtc || 543 !crtc_state->mode.hdisplay || !crtc_state->mode.vdisplay) 544 return 0; 545 546 /* Plane must be fully visible */ 547 if (new_plane_state->crtc_x < 0 || new_plane_state->crtc_y < 0 || 548 new_plane_state->crtc_x + new_plane_state->crtc_w > crtc_state->mode.hdisplay || 549 new_plane_state->crtc_y + new_plane_state->crtc_h > crtc_state->mode.vdisplay) 550 return -EINVAL; 551 552 /* Minimum size is 4x4 */ 553 if ((new_plane_state->src_w >> 16) < 4 || (new_plane_state->src_h >> 16) < 4) 554 return -EINVAL; 555 556 /* Input and output lines must have an even number of pixels. */ 557 if (((new_plane_state->src_w >> 16) & 1) || (new_plane_state->crtc_w & 1)) 558 return -EINVAL; 559 560 if (!osd_changed(new_plane_state, old_plane_state)) 561 return 0; 562 563 crtc_state->mode_changed = true; 564 565 xres = new_plane_state->src_w >> 16; 566 yres = new_plane_state->src_h >> 16; 567 568 /* 569 * Increase the scaled image's theorical width/height until we find a 570 * configuration that has valid scaling coefficients, up to 102% of the 571 * screen's resolution. This makes sure that we can scale from almost 572 * every resolution possible at the cost of a very small distorsion. 573 * The CRTC_W / CRTC_H are not modified. 574 */ 575 max_w = crtc_state->mode.hdisplay * 102 / 100; 576 max_h = crtc_state->mode.vdisplay * 102 / 100; 577 578 for (denom_w = xres, num_w = new_plane_state->crtc_w; num_w <= max_w; num_w++) 579 if (!reduce_fraction(&num_w, &denom_w)) 580 break; 581 if (num_w > max_w) 582 return -EINVAL; 583 584 for (denom_h = yres, num_h = new_plane_state->crtc_h; num_h <= max_h; num_h++) 585 if (!reduce_fraction(&num_h, &denom_h)) 586 break; 587 if (num_h > max_h) 588 return -EINVAL; 589 590 ipu->num_w = num_w; 591 ipu->num_h = num_h; 592 ipu->denom_w = denom_w; 593 ipu->denom_h = denom_h; 594 595 return 0; 596 } 597 598 static void ingenic_ipu_plane_atomic_disable(struct drm_plane *plane, 599 struct drm_atomic_state *state) 600 { 601 struct ingenic_ipu *ipu = plane_to_ingenic_ipu(plane); 602 603 regmap_set_bits(ipu->map, JZ_REG_IPU_CTRL, JZ_IPU_CTRL_STOP); 604 regmap_clear_bits(ipu->map, JZ_REG_IPU_CTRL, JZ_IPU_CTRL_CHIP_EN); 605 606 ingenic_drm_plane_disable(ipu->master, plane); 607 608 if (ipu->clk_enabled) { 609 clk_disable(ipu->clk); 610 ipu->clk_enabled = false; 611 } 612 } 613 614 static const struct drm_plane_helper_funcs ingenic_ipu_plane_helper_funcs = { 615 .atomic_update = ingenic_ipu_plane_atomic_update, 616 .atomic_check = ingenic_ipu_plane_atomic_check, 617 .atomic_disable = ingenic_ipu_plane_atomic_disable, 618 .prepare_fb = drm_gem_plane_helper_prepare_fb, 619 }; 620 621 static int 622 ingenic_ipu_plane_atomic_get_property(struct drm_plane *plane, 623 const struct drm_plane_state *state, 624 struct drm_property *property, u64 *val) 625 { 626 struct ingenic_ipu *ipu = plane_to_ingenic_ipu(plane); 627 628 if (property != ipu->sharpness_prop) 629 return -EINVAL; 630 631 *val = ipu->sharpness; 632 633 return 0; 634 } 635 636 static int 637 ingenic_ipu_plane_atomic_set_property(struct drm_plane *plane, 638 struct drm_plane_state *state, 639 struct drm_property *property, u64 val) 640 { 641 struct ingenic_ipu *ipu = plane_to_ingenic_ipu(plane); 642 struct drm_crtc_state *crtc_state; 643 644 if (property != ipu->sharpness_prop) 645 return -EINVAL; 646 647 ipu->sharpness = val; 648 649 if (state->crtc) { 650 crtc_state = drm_atomic_get_existing_crtc_state(state->state, state->crtc); 651 if (WARN_ON(!crtc_state)) 652 return -EINVAL; 653 654 crtc_state->mode_changed = true; 655 } 656 657 return 0; 658 } 659 660 static const struct drm_plane_funcs ingenic_ipu_plane_funcs = { 661 .update_plane = drm_atomic_helper_update_plane, 662 .disable_plane = drm_atomic_helper_disable_plane, 663 .reset = drm_atomic_helper_plane_reset, 664 .destroy = drm_plane_cleanup, 665 666 .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, 667 .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, 668 669 .atomic_get_property = ingenic_ipu_plane_atomic_get_property, 670 .atomic_set_property = ingenic_ipu_plane_atomic_set_property, 671 }; 672 673 static irqreturn_t ingenic_ipu_irq_handler(int irq, void *arg) 674 { 675 struct ingenic_ipu *ipu = arg; 676 struct drm_crtc *crtc = drm_crtc_from_index(ipu->drm, 0); 677 unsigned int dummy; 678 679 /* dummy read allows CPU to reconfigure IPU */ 680 if (ipu->soc_info->manual_restart) 681 regmap_read(ipu->map, JZ_REG_IPU_STATUS, &dummy); 682 683 /* ACK interrupt */ 684 regmap_write(ipu->map, JZ_REG_IPU_STATUS, 0); 685 686 /* Set previously cached addresses */ 687 regmap_write(ipu->map, JZ_REG_IPU_Y_ADDR, ipu->addr_y); 688 regmap_write(ipu->map, JZ_REG_IPU_U_ADDR, ipu->addr_u); 689 regmap_write(ipu->map, JZ_REG_IPU_V_ADDR, ipu->addr_v); 690 691 /* Run IPU for the new frame */ 692 if (ipu->soc_info->manual_restart) 693 regmap_set_bits(ipu->map, JZ_REG_IPU_CTRL, JZ_IPU_CTRL_RUN); 694 695 drm_crtc_handle_vblank(crtc); 696 697 return IRQ_HANDLED; 698 } 699 700 static const struct regmap_config ingenic_ipu_regmap_config = { 701 .reg_bits = 32, 702 .val_bits = 32, 703 .reg_stride = 4, 704 705 .max_register = JZ_REG_IPU_OUT_PHY_T_ADDR, 706 }; 707 708 static int ingenic_ipu_bind(struct device *dev, struct device *master, void *d) 709 { 710 struct platform_device *pdev = to_platform_device(dev); 711 const struct soc_info *soc_info; 712 struct drm_device *drm = d; 713 struct drm_plane *plane; 714 struct ingenic_ipu *ipu; 715 void __iomem *base; 716 unsigned int sharpness_max; 717 int err, irq; 718 719 ipu = devm_kzalloc(dev, sizeof(*ipu), GFP_KERNEL); 720 if (!ipu) 721 return -ENOMEM; 722 723 soc_info = of_device_get_match_data(dev); 724 if (!soc_info) { 725 dev_err(dev, "Missing platform data\n"); 726 return -EINVAL; 727 } 728 729 ipu->dev = dev; 730 ipu->drm = drm; 731 ipu->master = master; 732 ipu->soc_info = soc_info; 733 734 base = devm_platform_ioremap_resource(pdev, 0); 735 if (IS_ERR(base)) { 736 dev_err(dev, "Failed to get memory resource\n"); 737 return PTR_ERR(base); 738 } 739 740 ipu->map = devm_regmap_init_mmio(dev, base, &ingenic_ipu_regmap_config); 741 if (IS_ERR(ipu->map)) { 742 dev_err(dev, "Failed to create regmap\n"); 743 return PTR_ERR(ipu->map); 744 } 745 746 irq = platform_get_irq(pdev, 0); 747 if (irq < 0) 748 return irq; 749 750 ipu->clk = devm_clk_get(dev, "ipu"); 751 if (IS_ERR(ipu->clk)) { 752 dev_err(dev, "Failed to get pixel clock\n"); 753 return PTR_ERR(ipu->clk); 754 } 755 756 err = devm_request_irq(dev, irq, ingenic_ipu_irq_handler, 0, 757 dev_name(dev), ipu); 758 if (err) { 759 dev_err(dev, "Unable to request IRQ\n"); 760 return err; 761 } 762 763 plane = &ipu->plane; 764 dev_set_drvdata(dev, plane); 765 766 drm_plane_helper_add(plane, &ingenic_ipu_plane_helper_funcs); 767 768 err = drm_universal_plane_init(drm, plane, 1, &ingenic_ipu_plane_funcs, 769 soc_info->formats, soc_info->num_formats, 770 NULL, DRM_PLANE_TYPE_PRIMARY, NULL); 771 if (err) { 772 dev_err(dev, "Failed to init plane: %i\n", err); 773 return err; 774 } 775 776 /* 777 * Sharpness settings range is [0,32] 778 * 0 : nearest-neighbor 779 * 1 : bilinear 780 * 2 .. 32 : bicubic (translated to sharpness factor -0.25 .. -4.0) 781 */ 782 sharpness_max = soc_info->has_bicubic ? 32 : 1; 783 ipu->sharpness_prop = drm_property_create_range(drm, 0, "sharpness", 784 0, sharpness_max); 785 if (!ipu->sharpness_prop) { 786 dev_err(dev, "Unable to create sharpness property\n"); 787 return -ENOMEM; 788 } 789 790 /* Default sharpness factor: -0.125 * 8 = -1.0 */ 791 ipu->sharpness = soc_info->has_bicubic ? 8 : 1; 792 drm_object_attach_property(&plane->base, ipu->sharpness_prop, 793 ipu->sharpness); 794 795 err = clk_prepare(ipu->clk); 796 if (err) { 797 dev_err(dev, "Unable to prepare clock\n"); 798 return err; 799 } 800 801 return 0; 802 } 803 804 static void ingenic_ipu_unbind(struct device *dev, 805 struct device *master, void *d) 806 { 807 struct ingenic_ipu *ipu = dev_get_drvdata(dev); 808 809 clk_unprepare(ipu->clk); 810 } 811 812 static const struct component_ops ingenic_ipu_ops = { 813 .bind = ingenic_ipu_bind, 814 .unbind = ingenic_ipu_unbind, 815 }; 816 817 static int ingenic_ipu_probe(struct platform_device *pdev) 818 { 819 return component_add(&pdev->dev, &ingenic_ipu_ops); 820 } 821 822 static int ingenic_ipu_remove(struct platform_device *pdev) 823 { 824 component_del(&pdev->dev, &ingenic_ipu_ops); 825 return 0; 826 } 827 828 static const u32 jz4725b_ipu_formats[] = { 829 /* 830 * While officially supported, packed YUV 4:2:2 formats can cause 831 * random hardware crashes on JZ4725B under certain circumstances. 832 * It seems to happen with some specific resize ratios. 833 * Until a proper workaround or fix is found, disable these formats. 834 DRM_FORMAT_YUYV, 835 DRM_FORMAT_YVYU, 836 DRM_FORMAT_UYVY, 837 DRM_FORMAT_VYUY, 838 */ 839 DRM_FORMAT_YUV411, 840 DRM_FORMAT_YUV420, 841 DRM_FORMAT_YUV422, 842 DRM_FORMAT_YUV444, 843 }; 844 845 static const struct soc_info jz4725b_soc_info = { 846 .formats = jz4725b_ipu_formats, 847 .num_formats = ARRAY_SIZE(jz4725b_ipu_formats), 848 .has_bicubic = false, 849 .manual_restart = true, 850 .set_coefs = jz4725b_set_coefs, 851 }; 852 853 static const u32 jz4760_ipu_formats[] = { 854 DRM_FORMAT_XRGB1555, 855 DRM_FORMAT_XBGR1555, 856 DRM_FORMAT_RGB565, 857 DRM_FORMAT_BGR565, 858 DRM_FORMAT_XRGB8888, 859 DRM_FORMAT_XBGR8888, 860 DRM_FORMAT_YUYV, 861 DRM_FORMAT_YVYU, 862 DRM_FORMAT_UYVY, 863 DRM_FORMAT_VYUY, 864 DRM_FORMAT_YUV411, 865 DRM_FORMAT_YUV420, 866 DRM_FORMAT_YUV422, 867 DRM_FORMAT_YUV444, 868 DRM_FORMAT_XYUV8888, 869 }; 870 871 static const struct soc_info jz4760_soc_info = { 872 .formats = jz4760_ipu_formats, 873 .num_formats = ARRAY_SIZE(jz4760_ipu_formats), 874 .has_bicubic = true, 875 .manual_restart = false, 876 .set_coefs = jz4760_set_coefs, 877 }; 878 879 static const struct of_device_id ingenic_ipu_of_match[] = { 880 { .compatible = "ingenic,jz4725b-ipu", .data = &jz4725b_soc_info }, 881 { .compatible = "ingenic,jz4760-ipu", .data = &jz4760_soc_info }, 882 { /* sentinel */ }, 883 }; 884 MODULE_DEVICE_TABLE(of, ingenic_ipu_of_match); 885 886 static struct platform_driver ingenic_ipu_driver = { 887 .driver = { 888 .name = "ingenic-ipu", 889 .of_match_table = ingenic_ipu_of_match, 890 }, 891 .probe = ingenic_ipu_probe, 892 .remove = ingenic_ipu_remove, 893 }; 894 895 struct platform_driver *ingenic_ipu_driver_ptr = &ingenic_ipu_driver; 896