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