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