cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

ingenic-ipu.c (28359B)


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