162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci//
362306a36Sopenharmony_ci// Ingenic JZ47xx KMS driver
462306a36Sopenharmony_ci//
562306a36Sopenharmony_ci// Copyright (C) 2019, Paul Cercueil <paul@crapouillou.net>
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#include "ingenic-drm.h"
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <linux/bitfield.h>
1062306a36Sopenharmony_ci#include <linux/component.h>
1162306a36Sopenharmony_ci#include <linux/clk.h>
1262306a36Sopenharmony_ci#include <linux/dma-mapping.h>
1362306a36Sopenharmony_ci#include <linux/io.h>
1462306a36Sopenharmony_ci#include <linux/media-bus-format.h>
1562306a36Sopenharmony_ci#include <linux/module.h>
1662306a36Sopenharmony_ci#include <linux/mutex.h>
1762306a36Sopenharmony_ci#include <linux/of.h>
1862306a36Sopenharmony_ci#include <linux/of_reserved_mem.h>
1962306a36Sopenharmony_ci#include <linux/platform_device.h>
2062306a36Sopenharmony_ci#include <linux/pm.h>
2162306a36Sopenharmony_ci#include <linux/regmap.h>
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci#include <drm/drm_atomic.h>
2462306a36Sopenharmony_ci#include <drm/drm_atomic_helper.h>
2562306a36Sopenharmony_ci#include <drm/drm_bridge.h>
2662306a36Sopenharmony_ci#include <drm/drm_bridge_connector.h>
2762306a36Sopenharmony_ci#include <drm/drm_color_mgmt.h>
2862306a36Sopenharmony_ci#include <drm/drm_crtc.h>
2962306a36Sopenharmony_ci#include <drm/drm_damage_helper.h>
3062306a36Sopenharmony_ci#include <drm/drm_drv.h>
3162306a36Sopenharmony_ci#include <drm/drm_encoder.h>
3262306a36Sopenharmony_ci#include <drm/drm_gem_dma_helper.h>
3362306a36Sopenharmony_ci#include <drm/drm_fb_dma_helper.h>
3462306a36Sopenharmony_ci#include <drm/drm_fbdev_generic.h>
3562306a36Sopenharmony_ci#include <drm/drm_fourcc.h>
3662306a36Sopenharmony_ci#include <drm/drm_framebuffer.h>
3762306a36Sopenharmony_ci#include <drm/drm_gem_atomic_helper.h>
3862306a36Sopenharmony_ci#include <drm/drm_gem_framebuffer_helper.h>
3962306a36Sopenharmony_ci#include <drm/drm_managed.h>
4062306a36Sopenharmony_ci#include <drm/drm_of.h>
4162306a36Sopenharmony_ci#include <drm/drm_panel.h>
4262306a36Sopenharmony_ci#include <drm/drm_plane.h>
4362306a36Sopenharmony_ci#include <drm/drm_probe_helper.h>
4462306a36Sopenharmony_ci#include <drm/drm_vblank.h>
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci#define HWDESC_PALETTE 2
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_cistruct ingenic_dma_hwdesc {
4962306a36Sopenharmony_ci	u32 next;
5062306a36Sopenharmony_ci	u32 addr;
5162306a36Sopenharmony_ci	u32 id;
5262306a36Sopenharmony_ci	u32 cmd;
5362306a36Sopenharmony_ci	/* extended hw descriptor for jz4780 */
5462306a36Sopenharmony_ci	u32 offsize;
5562306a36Sopenharmony_ci	u32 pagewidth;
5662306a36Sopenharmony_ci	u32 cpos;
5762306a36Sopenharmony_ci	u32 dessize;
5862306a36Sopenharmony_ci} __aligned(16);
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_cistruct ingenic_dma_hwdescs {
6162306a36Sopenharmony_ci	struct ingenic_dma_hwdesc hwdesc[3];
6262306a36Sopenharmony_ci	u16 palette[256] __aligned(16);
6362306a36Sopenharmony_ci};
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_cistruct jz_soc_info {
6662306a36Sopenharmony_ci	bool needs_dev_clk;
6762306a36Sopenharmony_ci	bool has_osd;
6862306a36Sopenharmony_ci	bool has_alpha;
6962306a36Sopenharmony_ci	bool map_noncoherent;
7062306a36Sopenharmony_ci	bool use_extended_hwdesc;
7162306a36Sopenharmony_ci	bool plane_f0_not_working;
7262306a36Sopenharmony_ci	u32 max_burst;
7362306a36Sopenharmony_ci	unsigned int max_width, max_height;
7462306a36Sopenharmony_ci	const u32 *formats_f0, *formats_f1;
7562306a36Sopenharmony_ci	unsigned int num_formats_f0, num_formats_f1;
7662306a36Sopenharmony_ci};
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_cistruct ingenic_drm_private_state {
7962306a36Sopenharmony_ci	struct drm_private_state base;
8062306a36Sopenharmony_ci	bool use_palette;
8162306a36Sopenharmony_ci};
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_cistruct ingenic_drm {
8462306a36Sopenharmony_ci	struct drm_device drm;
8562306a36Sopenharmony_ci	/*
8662306a36Sopenharmony_ci	 * f1 (aka. foreground1) is our primary plane, on top of which
8762306a36Sopenharmony_ci	 * f0 (aka. foreground0) can be overlayed. Z-order is fixed in
8862306a36Sopenharmony_ci	 * hardware and cannot be changed.
8962306a36Sopenharmony_ci	 */
9062306a36Sopenharmony_ci	struct drm_plane f0, f1, *ipu_plane;
9162306a36Sopenharmony_ci	struct drm_crtc crtc;
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	struct device *dev;
9462306a36Sopenharmony_ci	struct regmap *map;
9562306a36Sopenharmony_ci	struct clk *lcd_clk, *pix_clk;
9662306a36Sopenharmony_ci	const struct jz_soc_info *soc_info;
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	struct ingenic_dma_hwdescs *dma_hwdescs;
9962306a36Sopenharmony_ci	dma_addr_t dma_hwdescs_phys;
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	bool panel_is_sharp;
10262306a36Sopenharmony_ci	bool no_vblank;
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	/*
10562306a36Sopenharmony_ci	 * clk_mutex is used to synchronize the pixel clock rate update with
10662306a36Sopenharmony_ci	 * the VBLANK. When the pixel clock's parent clock needs to be updated,
10762306a36Sopenharmony_ci	 * clock_nb's notifier function will lock the mutex, then wait until the
10862306a36Sopenharmony_ci	 * next VBLANK. At that point, the parent clock's rate can be updated,
10962306a36Sopenharmony_ci	 * and the mutex is then unlocked. If an atomic commit happens in the
11062306a36Sopenharmony_ci	 * meantime, it will lock on the mutex, effectively waiting until the
11162306a36Sopenharmony_ci	 * clock update process finishes. Finally, the pixel clock's rate will
11262306a36Sopenharmony_ci	 * be recomputed when the mutex has been released, in the pending atomic
11362306a36Sopenharmony_ci	 * commit, or a future one.
11462306a36Sopenharmony_ci	 */
11562306a36Sopenharmony_ci	struct mutex clk_mutex;
11662306a36Sopenharmony_ci	bool update_clk_rate;
11762306a36Sopenharmony_ci	struct notifier_block clock_nb;
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci	struct drm_private_obj private_obj;
12062306a36Sopenharmony_ci};
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_cistruct ingenic_drm_bridge {
12362306a36Sopenharmony_ci	struct drm_encoder encoder;
12462306a36Sopenharmony_ci	struct drm_bridge bridge, *next_bridge;
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci	struct drm_bus_cfg bus_cfg;
12762306a36Sopenharmony_ci};
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_cistatic inline struct ingenic_drm_bridge *
13062306a36Sopenharmony_cito_ingenic_drm_bridge(struct drm_encoder *encoder)
13162306a36Sopenharmony_ci{
13262306a36Sopenharmony_ci	return container_of(encoder, struct ingenic_drm_bridge, encoder);
13362306a36Sopenharmony_ci}
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_cistatic inline struct ingenic_drm_private_state *
13662306a36Sopenharmony_cito_ingenic_drm_priv_state(struct drm_private_state *state)
13762306a36Sopenharmony_ci{
13862306a36Sopenharmony_ci	return container_of(state, struct ingenic_drm_private_state, base);
13962306a36Sopenharmony_ci}
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_cistatic struct ingenic_drm_private_state *
14262306a36Sopenharmony_ciingenic_drm_get_priv_state(struct ingenic_drm *priv, struct drm_atomic_state *state)
14362306a36Sopenharmony_ci{
14462306a36Sopenharmony_ci	struct drm_private_state *priv_state;
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	priv_state = drm_atomic_get_private_obj_state(state, &priv->private_obj);
14762306a36Sopenharmony_ci	if (IS_ERR(priv_state))
14862306a36Sopenharmony_ci		return ERR_CAST(priv_state);
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	return to_ingenic_drm_priv_state(priv_state);
15162306a36Sopenharmony_ci}
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_cistatic struct ingenic_drm_private_state *
15462306a36Sopenharmony_ciingenic_drm_get_new_priv_state(struct ingenic_drm *priv, struct drm_atomic_state *state)
15562306a36Sopenharmony_ci{
15662306a36Sopenharmony_ci	struct drm_private_state *priv_state;
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	priv_state = drm_atomic_get_new_private_obj_state(state, &priv->private_obj);
15962306a36Sopenharmony_ci	if (!priv_state)
16062306a36Sopenharmony_ci		return NULL;
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci	return to_ingenic_drm_priv_state(priv_state);
16362306a36Sopenharmony_ci}
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_cistatic bool ingenic_drm_writeable_reg(struct device *dev, unsigned int reg)
16662306a36Sopenharmony_ci{
16762306a36Sopenharmony_ci	switch (reg) {
16862306a36Sopenharmony_ci	case JZ_REG_LCD_IID:
16962306a36Sopenharmony_ci	case JZ_REG_LCD_SA0:
17062306a36Sopenharmony_ci	case JZ_REG_LCD_FID0:
17162306a36Sopenharmony_ci	case JZ_REG_LCD_CMD0:
17262306a36Sopenharmony_ci	case JZ_REG_LCD_SA1:
17362306a36Sopenharmony_ci	case JZ_REG_LCD_FID1:
17462306a36Sopenharmony_ci	case JZ_REG_LCD_CMD1:
17562306a36Sopenharmony_ci		return false;
17662306a36Sopenharmony_ci	default:
17762306a36Sopenharmony_ci		return true;
17862306a36Sopenharmony_ci	}
17962306a36Sopenharmony_ci}
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_cistatic const struct regmap_config ingenic_drm_regmap_config = {
18262306a36Sopenharmony_ci	.reg_bits = 32,
18362306a36Sopenharmony_ci	.val_bits = 32,
18462306a36Sopenharmony_ci	.reg_stride = 4,
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci	.writeable_reg = ingenic_drm_writeable_reg,
18762306a36Sopenharmony_ci};
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_cistatic inline struct ingenic_drm *drm_device_get_priv(struct drm_device *drm)
19062306a36Sopenharmony_ci{
19162306a36Sopenharmony_ci	return container_of(drm, struct ingenic_drm, drm);
19262306a36Sopenharmony_ci}
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_cistatic inline struct ingenic_drm *drm_crtc_get_priv(struct drm_crtc *crtc)
19562306a36Sopenharmony_ci{
19662306a36Sopenharmony_ci	return container_of(crtc, struct ingenic_drm, crtc);
19762306a36Sopenharmony_ci}
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_cistatic inline struct ingenic_drm *drm_nb_get_priv(struct notifier_block *nb)
20062306a36Sopenharmony_ci{
20162306a36Sopenharmony_ci	return container_of(nb, struct ingenic_drm, clock_nb);
20262306a36Sopenharmony_ci}
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_cistatic inline dma_addr_t dma_hwdesc_addr(const struct ingenic_drm *priv,
20562306a36Sopenharmony_ci					 unsigned int idx)
20662306a36Sopenharmony_ci{
20762306a36Sopenharmony_ci	u32 offset = offsetof(struct ingenic_dma_hwdescs, hwdesc[idx]);
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci	return priv->dma_hwdescs_phys + offset;
21062306a36Sopenharmony_ci}
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_cistatic int ingenic_drm_update_pixclk(struct notifier_block *nb,
21362306a36Sopenharmony_ci				     unsigned long action,
21462306a36Sopenharmony_ci				     void *data)
21562306a36Sopenharmony_ci{
21662306a36Sopenharmony_ci	struct ingenic_drm *priv = drm_nb_get_priv(nb);
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci	switch (action) {
21962306a36Sopenharmony_ci	case PRE_RATE_CHANGE:
22062306a36Sopenharmony_ci		mutex_lock(&priv->clk_mutex);
22162306a36Sopenharmony_ci		priv->update_clk_rate = true;
22262306a36Sopenharmony_ci		drm_crtc_wait_one_vblank(&priv->crtc);
22362306a36Sopenharmony_ci		return NOTIFY_OK;
22462306a36Sopenharmony_ci	default:
22562306a36Sopenharmony_ci		mutex_unlock(&priv->clk_mutex);
22662306a36Sopenharmony_ci		return NOTIFY_OK;
22762306a36Sopenharmony_ci	}
22862306a36Sopenharmony_ci}
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_cistatic void ingenic_drm_bridge_atomic_enable(struct drm_bridge *bridge,
23162306a36Sopenharmony_ci					     struct drm_bridge_state *old_bridge_state)
23262306a36Sopenharmony_ci{
23362306a36Sopenharmony_ci	struct ingenic_drm *priv = drm_device_get_priv(bridge->dev);
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	regmap_write(priv->map, JZ_REG_LCD_STATE, 0);
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci	regmap_update_bits(priv->map, JZ_REG_LCD_CTRL,
23862306a36Sopenharmony_ci			   JZ_LCD_CTRL_ENABLE | JZ_LCD_CTRL_DISABLE,
23962306a36Sopenharmony_ci			   JZ_LCD_CTRL_ENABLE);
24062306a36Sopenharmony_ci}
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_cistatic void ingenic_drm_crtc_atomic_enable(struct drm_crtc *crtc,
24362306a36Sopenharmony_ci					   struct drm_atomic_state *state)
24462306a36Sopenharmony_ci{
24562306a36Sopenharmony_ci	struct ingenic_drm *priv = drm_crtc_get_priv(crtc);
24662306a36Sopenharmony_ci	struct ingenic_drm_private_state *priv_state;
24762306a36Sopenharmony_ci	unsigned int next_id;
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci	priv_state = ingenic_drm_get_priv_state(priv, state);
25062306a36Sopenharmony_ci	if (WARN_ON(IS_ERR(priv_state)))
25162306a36Sopenharmony_ci		return;
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci	/* Set addresses of our DMA descriptor chains */
25462306a36Sopenharmony_ci	next_id = priv_state->use_palette ? HWDESC_PALETTE : 0;
25562306a36Sopenharmony_ci	regmap_write(priv->map, JZ_REG_LCD_DA0, dma_hwdesc_addr(priv, next_id));
25662306a36Sopenharmony_ci	regmap_write(priv->map, JZ_REG_LCD_DA1, dma_hwdesc_addr(priv, 1));
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci	drm_crtc_vblank_on(crtc);
25962306a36Sopenharmony_ci}
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_cistatic void ingenic_drm_bridge_atomic_disable(struct drm_bridge *bridge,
26262306a36Sopenharmony_ci					      struct drm_bridge_state *old_bridge_state)
26362306a36Sopenharmony_ci{
26462306a36Sopenharmony_ci	struct ingenic_drm *priv = drm_device_get_priv(bridge->dev);
26562306a36Sopenharmony_ci	unsigned int var;
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci	regmap_update_bits(priv->map, JZ_REG_LCD_CTRL,
26862306a36Sopenharmony_ci			   JZ_LCD_CTRL_DISABLE, JZ_LCD_CTRL_DISABLE);
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	regmap_read_poll_timeout(priv->map, JZ_REG_LCD_STATE, var,
27162306a36Sopenharmony_ci				 var & JZ_LCD_STATE_DISABLED,
27262306a36Sopenharmony_ci				 1000, 0);
27362306a36Sopenharmony_ci}
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_cistatic void ingenic_drm_crtc_atomic_disable(struct drm_crtc *crtc,
27662306a36Sopenharmony_ci					    struct drm_atomic_state *state)
27762306a36Sopenharmony_ci{
27862306a36Sopenharmony_ci	drm_crtc_vblank_off(crtc);
27962306a36Sopenharmony_ci}
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_cistatic void ingenic_drm_crtc_update_timings(struct ingenic_drm *priv,
28262306a36Sopenharmony_ci					    struct drm_display_mode *mode)
28362306a36Sopenharmony_ci{
28462306a36Sopenharmony_ci	unsigned int vpe, vds, vde, vt, hpe, hds, hde, ht;
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci	vpe = mode->crtc_vsync_end - mode->crtc_vsync_start;
28762306a36Sopenharmony_ci	vds = mode->crtc_vtotal - mode->crtc_vsync_start;
28862306a36Sopenharmony_ci	vde = vds + mode->crtc_vdisplay;
28962306a36Sopenharmony_ci	vt = vde + mode->crtc_vsync_start - mode->crtc_vdisplay;
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ci	hpe = mode->crtc_hsync_end - mode->crtc_hsync_start;
29262306a36Sopenharmony_ci	hds = mode->crtc_htotal - mode->crtc_hsync_start;
29362306a36Sopenharmony_ci	hde = hds + mode->crtc_hdisplay;
29462306a36Sopenharmony_ci	ht = hde + mode->crtc_hsync_start - mode->crtc_hdisplay;
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci	regmap_write(priv->map, JZ_REG_LCD_VSYNC,
29762306a36Sopenharmony_ci		     0 << JZ_LCD_VSYNC_VPS_OFFSET |
29862306a36Sopenharmony_ci		     vpe << JZ_LCD_VSYNC_VPE_OFFSET);
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci	regmap_write(priv->map, JZ_REG_LCD_HSYNC,
30162306a36Sopenharmony_ci		     0 << JZ_LCD_HSYNC_HPS_OFFSET |
30262306a36Sopenharmony_ci		     hpe << JZ_LCD_HSYNC_HPE_OFFSET);
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci	regmap_write(priv->map, JZ_REG_LCD_VAT,
30562306a36Sopenharmony_ci		     ht << JZ_LCD_VAT_HT_OFFSET |
30662306a36Sopenharmony_ci		     vt << JZ_LCD_VAT_VT_OFFSET);
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ci	regmap_write(priv->map, JZ_REG_LCD_DAH,
30962306a36Sopenharmony_ci		     hds << JZ_LCD_DAH_HDS_OFFSET |
31062306a36Sopenharmony_ci		     hde << JZ_LCD_DAH_HDE_OFFSET);
31162306a36Sopenharmony_ci	regmap_write(priv->map, JZ_REG_LCD_DAV,
31262306a36Sopenharmony_ci		     vds << JZ_LCD_DAV_VDS_OFFSET |
31362306a36Sopenharmony_ci		     vde << JZ_LCD_DAV_VDE_OFFSET);
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci	if (priv->panel_is_sharp) {
31662306a36Sopenharmony_ci		regmap_write(priv->map, JZ_REG_LCD_PS, hde << 16 | (hde + 1));
31762306a36Sopenharmony_ci		regmap_write(priv->map, JZ_REG_LCD_CLS, hde << 16 | (hde + 1));
31862306a36Sopenharmony_ci		regmap_write(priv->map, JZ_REG_LCD_SPL, hpe << 16 | (hpe + 1));
31962306a36Sopenharmony_ci		regmap_write(priv->map, JZ_REG_LCD_REV, mode->htotal << 16);
32062306a36Sopenharmony_ci	}
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci	regmap_update_bits(priv->map, JZ_REG_LCD_CTRL,
32362306a36Sopenharmony_ci			   JZ_LCD_CTRL_OFUP | JZ_LCD_CTRL_BURST_MASK,
32462306a36Sopenharmony_ci			   JZ_LCD_CTRL_OFUP | priv->soc_info->max_burst);
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_ci	/*
32762306a36Sopenharmony_ci	 * IPU restart - specify how much time the LCDC will wait before
32862306a36Sopenharmony_ci	 * transferring a new frame from the IPU. The value is the one
32962306a36Sopenharmony_ci	 * suggested in the programming manual.
33062306a36Sopenharmony_ci	 */
33162306a36Sopenharmony_ci	regmap_write(priv->map, JZ_REG_LCD_IPUR, JZ_LCD_IPUR_IPUREN |
33262306a36Sopenharmony_ci		     (ht * vpe / 3) << JZ_LCD_IPUR_IPUR_LSB);
33362306a36Sopenharmony_ci}
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_cistatic int ingenic_drm_crtc_atomic_check(struct drm_crtc *crtc,
33662306a36Sopenharmony_ci					 struct drm_atomic_state *state)
33762306a36Sopenharmony_ci{
33862306a36Sopenharmony_ci	struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state,
33962306a36Sopenharmony_ci									  crtc);
34062306a36Sopenharmony_ci	struct ingenic_drm *priv = drm_crtc_get_priv(crtc);
34162306a36Sopenharmony_ci	struct drm_plane_state *f1_state, *f0_state, *ipu_state = NULL;
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci	if (crtc_state->gamma_lut &&
34462306a36Sopenharmony_ci	    drm_color_lut_size(crtc_state->gamma_lut) != ARRAY_SIZE(priv->dma_hwdescs->palette)) {
34562306a36Sopenharmony_ci		dev_dbg(priv->dev, "Invalid palette size\n");
34662306a36Sopenharmony_ci		return -EINVAL;
34762306a36Sopenharmony_ci	}
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci	if (drm_atomic_crtc_needs_modeset(crtc_state) && priv->soc_info->has_osd) {
35062306a36Sopenharmony_ci		f1_state = drm_atomic_get_plane_state(crtc_state->state,
35162306a36Sopenharmony_ci						      &priv->f1);
35262306a36Sopenharmony_ci		if (IS_ERR(f1_state))
35362306a36Sopenharmony_ci			return PTR_ERR(f1_state);
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci		f0_state = drm_atomic_get_plane_state(crtc_state->state,
35662306a36Sopenharmony_ci						      &priv->f0);
35762306a36Sopenharmony_ci		if (IS_ERR(f0_state))
35862306a36Sopenharmony_ci			return PTR_ERR(f0_state);
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci		if (IS_ENABLED(CONFIG_DRM_INGENIC_IPU) && priv->ipu_plane) {
36162306a36Sopenharmony_ci			ipu_state = drm_atomic_get_plane_state(crtc_state->state,
36262306a36Sopenharmony_ci							       priv->ipu_plane);
36362306a36Sopenharmony_ci			if (IS_ERR(ipu_state))
36462306a36Sopenharmony_ci				return PTR_ERR(ipu_state);
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci			/* IPU and F1 planes cannot be enabled at the same time. */
36762306a36Sopenharmony_ci			if (f1_state->fb && ipu_state->fb) {
36862306a36Sopenharmony_ci				dev_dbg(priv->dev, "Cannot enable both F1 and IPU\n");
36962306a36Sopenharmony_ci				return -EINVAL;
37062306a36Sopenharmony_ci			}
37162306a36Sopenharmony_ci		}
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_ci		/* If all the planes are disabled, we won't get a VBLANK IRQ */
37462306a36Sopenharmony_ci		priv->no_vblank = !f1_state->fb && !f0_state->fb &&
37562306a36Sopenharmony_ci				  !(ipu_state && ipu_state->fb);
37662306a36Sopenharmony_ci	}
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci	return 0;
37962306a36Sopenharmony_ci}
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_cistatic enum drm_mode_status
38262306a36Sopenharmony_ciingenic_drm_crtc_mode_valid(struct drm_crtc *crtc, const struct drm_display_mode *mode)
38362306a36Sopenharmony_ci{
38462306a36Sopenharmony_ci	struct ingenic_drm *priv = drm_crtc_get_priv(crtc);
38562306a36Sopenharmony_ci	long rate;
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ci	if (mode->hdisplay > priv->soc_info->max_width)
38862306a36Sopenharmony_ci		return MODE_BAD_HVALUE;
38962306a36Sopenharmony_ci	if (mode->vdisplay > priv->soc_info->max_height)
39062306a36Sopenharmony_ci		return MODE_BAD_VVALUE;
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_ci	rate = clk_round_rate(priv->pix_clk, mode->clock * 1000);
39362306a36Sopenharmony_ci	if (rate < 0)
39462306a36Sopenharmony_ci		return MODE_CLOCK_RANGE;
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci	return MODE_OK;
39762306a36Sopenharmony_ci}
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_cistatic void ingenic_drm_crtc_atomic_begin(struct drm_crtc *crtc,
40062306a36Sopenharmony_ci					  struct drm_atomic_state *state)
40162306a36Sopenharmony_ci{
40262306a36Sopenharmony_ci	struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state,
40362306a36Sopenharmony_ci									  crtc);
40462306a36Sopenharmony_ci	struct ingenic_drm *priv = drm_crtc_get_priv(crtc);
40562306a36Sopenharmony_ci	u32 ctrl = 0;
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci	if (priv->soc_info->has_osd &&
40862306a36Sopenharmony_ci	    drm_atomic_crtc_needs_modeset(crtc_state)) {
40962306a36Sopenharmony_ci		/*
41062306a36Sopenharmony_ci		 * If IPU plane is enabled, enable IPU as source for the F1
41162306a36Sopenharmony_ci		 * plane; otherwise use regular DMA.
41262306a36Sopenharmony_ci		 */
41362306a36Sopenharmony_ci		if (priv->ipu_plane && priv->ipu_plane->state->fb)
41462306a36Sopenharmony_ci			ctrl |= JZ_LCD_OSDCTRL_IPU;
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_ci		regmap_update_bits(priv->map, JZ_REG_LCD_OSDCTRL,
41762306a36Sopenharmony_ci				   JZ_LCD_OSDCTRL_IPU, ctrl);
41862306a36Sopenharmony_ci	}
41962306a36Sopenharmony_ci}
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_cistatic void ingenic_drm_crtc_atomic_flush(struct drm_crtc *crtc,
42262306a36Sopenharmony_ci					  struct drm_atomic_state *state)
42362306a36Sopenharmony_ci{
42462306a36Sopenharmony_ci	struct ingenic_drm *priv = drm_crtc_get_priv(crtc);
42562306a36Sopenharmony_ci	struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state,
42662306a36Sopenharmony_ci									  crtc);
42762306a36Sopenharmony_ci	struct drm_pending_vblank_event *event = crtc_state->event;
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ci	if (drm_atomic_crtc_needs_modeset(crtc_state)) {
43062306a36Sopenharmony_ci		ingenic_drm_crtc_update_timings(priv, &crtc_state->adjusted_mode);
43162306a36Sopenharmony_ci		priv->update_clk_rate = true;
43262306a36Sopenharmony_ci	}
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ci	if (priv->update_clk_rate) {
43562306a36Sopenharmony_ci		mutex_lock(&priv->clk_mutex);
43662306a36Sopenharmony_ci		clk_set_rate(priv->pix_clk,
43762306a36Sopenharmony_ci			     crtc_state->adjusted_mode.crtc_clock * 1000);
43862306a36Sopenharmony_ci		priv->update_clk_rate = false;
43962306a36Sopenharmony_ci		mutex_unlock(&priv->clk_mutex);
44062306a36Sopenharmony_ci	}
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_ci	if (event) {
44362306a36Sopenharmony_ci		crtc_state->event = NULL;
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_ci		spin_lock_irq(&crtc->dev->event_lock);
44662306a36Sopenharmony_ci		if (drm_crtc_vblank_get(crtc) == 0)
44762306a36Sopenharmony_ci			drm_crtc_arm_vblank_event(crtc, event);
44862306a36Sopenharmony_ci		else
44962306a36Sopenharmony_ci			drm_crtc_send_vblank_event(crtc, event);
45062306a36Sopenharmony_ci		spin_unlock_irq(&crtc->dev->event_lock);
45162306a36Sopenharmony_ci	}
45262306a36Sopenharmony_ci}
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_cistatic int ingenic_drm_plane_atomic_check(struct drm_plane *plane,
45562306a36Sopenharmony_ci					  struct drm_atomic_state *state)
45662306a36Sopenharmony_ci{
45762306a36Sopenharmony_ci	struct drm_plane_state *old_plane_state = drm_atomic_get_old_plane_state(state,
45862306a36Sopenharmony_ci										 plane);
45962306a36Sopenharmony_ci	struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
46062306a36Sopenharmony_ci										 plane);
46162306a36Sopenharmony_ci	struct ingenic_drm *priv = drm_device_get_priv(plane->dev);
46262306a36Sopenharmony_ci	struct ingenic_drm_private_state *priv_state;
46362306a36Sopenharmony_ci	struct drm_crtc_state *crtc_state;
46462306a36Sopenharmony_ci	struct drm_crtc *crtc = new_plane_state->crtc ?: old_plane_state->crtc;
46562306a36Sopenharmony_ci	int ret;
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci	if (!crtc)
46862306a36Sopenharmony_ci		return 0;
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_ci	if (priv->soc_info->plane_f0_not_working && plane == &priv->f0)
47162306a36Sopenharmony_ci		return -EINVAL;
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_ci	crtc_state = drm_atomic_get_existing_crtc_state(state,
47462306a36Sopenharmony_ci							crtc);
47562306a36Sopenharmony_ci	if (WARN_ON(!crtc_state))
47662306a36Sopenharmony_ci		return -EINVAL;
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_ci	priv_state = ingenic_drm_get_priv_state(priv, state);
47962306a36Sopenharmony_ci	if (IS_ERR(priv_state))
48062306a36Sopenharmony_ci		return PTR_ERR(priv_state);
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_ci	ret = drm_atomic_helper_check_plane_state(new_plane_state, crtc_state,
48362306a36Sopenharmony_ci						  DRM_PLANE_NO_SCALING,
48462306a36Sopenharmony_ci						  DRM_PLANE_NO_SCALING,
48562306a36Sopenharmony_ci						  priv->soc_info->has_osd,
48662306a36Sopenharmony_ci						  true);
48762306a36Sopenharmony_ci	if (ret)
48862306a36Sopenharmony_ci		return ret;
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_ci	/*
49162306a36Sopenharmony_ci	 * If OSD is not available, check that the width/height match.
49262306a36Sopenharmony_ci	 * Note that state->src_* are in 16.16 fixed-point format.
49362306a36Sopenharmony_ci	 */
49462306a36Sopenharmony_ci	if (!priv->soc_info->has_osd &&
49562306a36Sopenharmony_ci	    (new_plane_state->src_x != 0 ||
49662306a36Sopenharmony_ci	     (new_plane_state->src_w >> 16) != new_plane_state->crtc_w ||
49762306a36Sopenharmony_ci	     (new_plane_state->src_h >> 16) != new_plane_state->crtc_h))
49862306a36Sopenharmony_ci		return -EINVAL;
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_ci	priv_state->use_palette = new_plane_state->fb &&
50162306a36Sopenharmony_ci		new_plane_state->fb->format->format == DRM_FORMAT_C8;
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_ci	/*
50462306a36Sopenharmony_ci	 * Require full modeset if enabling or disabling a plane, or changing
50562306a36Sopenharmony_ci	 * its position, size or depth.
50662306a36Sopenharmony_ci	 */
50762306a36Sopenharmony_ci	if (priv->soc_info->has_osd &&
50862306a36Sopenharmony_ci	    (!old_plane_state->fb || !new_plane_state->fb ||
50962306a36Sopenharmony_ci	     old_plane_state->crtc_x != new_plane_state->crtc_x ||
51062306a36Sopenharmony_ci	     old_plane_state->crtc_y != new_plane_state->crtc_y ||
51162306a36Sopenharmony_ci	     old_plane_state->crtc_w != new_plane_state->crtc_w ||
51262306a36Sopenharmony_ci	     old_plane_state->crtc_h != new_plane_state->crtc_h ||
51362306a36Sopenharmony_ci	     old_plane_state->fb->format->format != new_plane_state->fb->format->format))
51462306a36Sopenharmony_ci		crtc_state->mode_changed = true;
51562306a36Sopenharmony_ci
51662306a36Sopenharmony_ci	if (priv->soc_info->map_noncoherent)
51762306a36Sopenharmony_ci		drm_atomic_helper_check_plane_damage(state, new_plane_state);
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_ci	return 0;
52062306a36Sopenharmony_ci}
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_cistatic void ingenic_drm_plane_enable(struct ingenic_drm *priv,
52362306a36Sopenharmony_ci				     struct drm_plane *plane)
52462306a36Sopenharmony_ci{
52562306a36Sopenharmony_ci	unsigned int en_bit;
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_ci	if (priv->soc_info->has_osd) {
52862306a36Sopenharmony_ci		if (plane != &priv->f0)
52962306a36Sopenharmony_ci			en_bit = JZ_LCD_OSDC_F1EN;
53062306a36Sopenharmony_ci		else
53162306a36Sopenharmony_ci			en_bit = JZ_LCD_OSDC_F0EN;
53262306a36Sopenharmony_ci
53362306a36Sopenharmony_ci		regmap_set_bits(priv->map, JZ_REG_LCD_OSDC, en_bit);
53462306a36Sopenharmony_ci	}
53562306a36Sopenharmony_ci}
53662306a36Sopenharmony_ci
53762306a36Sopenharmony_civoid ingenic_drm_plane_disable(struct device *dev, struct drm_plane *plane)
53862306a36Sopenharmony_ci{
53962306a36Sopenharmony_ci	struct ingenic_drm *priv = dev_get_drvdata(dev);
54062306a36Sopenharmony_ci	unsigned int en_bit;
54162306a36Sopenharmony_ci
54262306a36Sopenharmony_ci	if (priv->soc_info->has_osd) {
54362306a36Sopenharmony_ci		if (plane != &priv->f0)
54462306a36Sopenharmony_ci			en_bit = JZ_LCD_OSDC_F1EN;
54562306a36Sopenharmony_ci		else
54662306a36Sopenharmony_ci			en_bit = JZ_LCD_OSDC_F0EN;
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_ci		regmap_clear_bits(priv->map, JZ_REG_LCD_OSDC, en_bit);
54962306a36Sopenharmony_ci	}
55062306a36Sopenharmony_ci}
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_cistatic void ingenic_drm_plane_atomic_disable(struct drm_plane *plane,
55362306a36Sopenharmony_ci					     struct drm_atomic_state *state)
55462306a36Sopenharmony_ci{
55562306a36Sopenharmony_ci	struct ingenic_drm *priv = drm_device_get_priv(plane->dev);
55662306a36Sopenharmony_ci
55762306a36Sopenharmony_ci	ingenic_drm_plane_disable(priv->dev, plane);
55862306a36Sopenharmony_ci}
55962306a36Sopenharmony_ci
56062306a36Sopenharmony_civoid ingenic_drm_plane_config(struct device *dev,
56162306a36Sopenharmony_ci			      struct drm_plane *plane, u32 fourcc)
56262306a36Sopenharmony_ci{
56362306a36Sopenharmony_ci	struct ingenic_drm *priv = dev_get_drvdata(dev);
56462306a36Sopenharmony_ci	struct drm_plane_state *state = plane->state;
56562306a36Sopenharmony_ci	unsigned int xy_reg, size_reg;
56662306a36Sopenharmony_ci	unsigned int ctrl = 0;
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ci	ingenic_drm_plane_enable(priv, plane);
56962306a36Sopenharmony_ci
57062306a36Sopenharmony_ci	if (priv->soc_info->has_osd && plane != &priv->f0) {
57162306a36Sopenharmony_ci		switch (fourcc) {
57262306a36Sopenharmony_ci		case DRM_FORMAT_XRGB1555:
57362306a36Sopenharmony_ci			ctrl |= JZ_LCD_OSDCTRL_RGB555;
57462306a36Sopenharmony_ci			fallthrough;
57562306a36Sopenharmony_ci		case DRM_FORMAT_RGB565:
57662306a36Sopenharmony_ci			ctrl |= JZ_LCD_OSDCTRL_BPP_15_16;
57762306a36Sopenharmony_ci			break;
57862306a36Sopenharmony_ci		case DRM_FORMAT_RGB888:
57962306a36Sopenharmony_ci			ctrl |= JZ_LCD_OSDCTRL_BPP_24_COMP;
58062306a36Sopenharmony_ci			break;
58162306a36Sopenharmony_ci		case DRM_FORMAT_XRGB8888:
58262306a36Sopenharmony_ci			ctrl |= JZ_LCD_OSDCTRL_BPP_18_24;
58362306a36Sopenharmony_ci			break;
58462306a36Sopenharmony_ci		case DRM_FORMAT_XRGB2101010:
58562306a36Sopenharmony_ci			ctrl |= JZ_LCD_OSDCTRL_BPP_30;
58662306a36Sopenharmony_ci			break;
58762306a36Sopenharmony_ci		}
58862306a36Sopenharmony_ci
58962306a36Sopenharmony_ci		regmap_update_bits(priv->map, JZ_REG_LCD_OSDCTRL,
59062306a36Sopenharmony_ci				   JZ_LCD_OSDCTRL_BPP_MASK, ctrl);
59162306a36Sopenharmony_ci	} else {
59262306a36Sopenharmony_ci		switch (fourcc) {
59362306a36Sopenharmony_ci		case DRM_FORMAT_C8:
59462306a36Sopenharmony_ci			ctrl |= JZ_LCD_CTRL_BPP_8;
59562306a36Sopenharmony_ci			break;
59662306a36Sopenharmony_ci		case DRM_FORMAT_XRGB1555:
59762306a36Sopenharmony_ci			ctrl |= JZ_LCD_CTRL_RGB555;
59862306a36Sopenharmony_ci			fallthrough;
59962306a36Sopenharmony_ci		case DRM_FORMAT_RGB565:
60062306a36Sopenharmony_ci			ctrl |= JZ_LCD_CTRL_BPP_15_16;
60162306a36Sopenharmony_ci			break;
60262306a36Sopenharmony_ci		case DRM_FORMAT_RGB888:
60362306a36Sopenharmony_ci			ctrl |= JZ_LCD_CTRL_BPP_24_COMP;
60462306a36Sopenharmony_ci			break;
60562306a36Sopenharmony_ci		case DRM_FORMAT_XRGB8888:
60662306a36Sopenharmony_ci			ctrl |= JZ_LCD_CTRL_BPP_18_24;
60762306a36Sopenharmony_ci			break;
60862306a36Sopenharmony_ci		case DRM_FORMAT_XRGB2101010:
60962306a36Sopenharmony_ci			ctrl |= JZ_LCD_CTRL_BPP_30;
61062306a36Sopenharmony_ci			break;
61162306a36Sopenharmony_ci		}
61262306a36Sopenharmony_ci
61362306a36Sopenharmony_ci		regmap_update_bits(priv->map, JZ_REG_LCD_CTRL,
61462306a36Sopenharmony_ci				   JZ_LCD_CTRL_BPP_MASK, ctrl);
61562306a36Sopenharmony_ci	}
61662306a36Sopenharmony_ci
61762306a36Sopenharmony_ci	if (priv->soc_info->has_osd) {
61862306a36Sopenharmony_ci		if (plane != &priv->f0) {
61962306a36Sopenharmony_ci			xy_reg = JZ_REG_LCD_XYP1;
62062306a36Sopenharmony_ci			size_reg = JZ_REG_LCD_SIZE1;
62162306a36Sopenharmony_ci		} else {
62262306a36Sopenharmony_ci			xy_reg = JZ_REG_LCD_XYP0;
62362306a36Sopenharmony_ci			size_reg = JZ_REG_LCD_SIZE0;
62462306a36Sopenharmony_ci		}
62562306a36Sopenharmony_ci
62662306a36Sopenharmony_ci		regmap_write(priv->map, xy_reg,
62762306a36Sopenharmony_ci			     state->crtc_x << JZ_LCD_XYP01_XPOS_LSB |
62862306a36Sopenharmony_ci			     state->crtc_y << JZ_LCD_XYP01_YPOS_LSB);
62962306a36Sopenharmony_ci		regmap_write(priv->map, size_reg,
63062306a36Sopenharmony_ci			     state->crtc_w << JZ_LCD_SIZE01_WIDTH_LSB |
63162306a36Sopenharmony_ci			     state->crtc_h << JZ_LCD_SIZE01_HEIGHT_LSB);
63262306a36Sopenharmony_ci	}
63362306a36Sopenharmony_ci}
63462306a36Sopenharmony_ci
63562306a36Sopenharmony_cibool ingenic_drm_map_noncoherent(const struct device *dev)
63662306a36Sopenharmony_ci{
63762306a36Sopenharmony_ci	const struct ingenic_drm *priv = dev_get_drvdata(dev);
63862306a36Sopenharmony_ci
63962306a36Sopenharmony_ci	return priv->soc_info->map_noncoherent;
64062306a36Sopenharmony_ci}
64162306a36Sopenharmony_ci
64262306a36Sopenharmony_cistatic void ingenic_drm_update_palette(struct ingenic_drm *priv,
64362306a36Sopenharmony_ci				       const struct drm_color_lut *lut)
64462306a36Sopenharmony_ci{
64562306a36Sopenharmony_ci	unsigned int i;
64662306a36Sopenharmony_ci
64762306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(priv->dma_hwdescs->palette); i++) {
64862306a36Sopenharmony_ci		u16 color = drm_color_lut_extract(lut[i].red, 5) << 11
64962306a36Sopenharmony_ci			| drm_color_lut_extract(lut[i].green, 6) << 5
65062306a36Sopenharmony_ci			| drm_color_lut_extract(lut[i].blue, 5);
65162306a36Sopenharmony_ci
65262306a36Sopenharmony_ci		priv->dma_hwdescs->palette[i] = color;
65362306a36Sopenharmony_ci	}
65462306a36Sopenharmony_ci}
65562306a36Sopenharmony_ci
65662306a36Sopenharmony_cistatic void ingenic_drm_plane_atomic_update(struct drm_plane *plane,
65762306a36Sopenharmony_ci					    struct drm_atomic_state *state)
65862306a36Sopenharmony_ci{
65962306a36Sopenharmony_ci	struct ingenic_drm *priv = drm_device_get_priv(plane->dev);
66062306a36Sopenharmony_ci	struct drm_plane_state *newstate = drm_atomic_get_new_plane_state(state, plane);
66162306a36Sopenharmony_ci	struct drm_plane_state *oldstate = drm_atomic_get_old_plane_state(state, plane);
66262306a36Sopenharmony_ci	unsigned int width, height, cpp, next_id, plane_id;
66362306a36Sopenharmony_ci	struct ingenic_drm_private_state *priv_state;
66462306a36Sopenharmony_ci	struct drm_crtc_state *crtc_state;
66562306a36Sopenharmony_ci	struct ingenic_dma_hwdesc *hwdesc;
66662306a36Sopenharmony_ci	dma_addr_t addr;
66762306a36Sopenharmony_ci	u32 fourcc;
66862306a36Sopenharmony_ci
66962306a36Sopenharmony_ci	if (newstate && newstate->fb) {
67062306a36Sopenharmony_ci		if (priv->soc_info->map_noncoherent)
67162306a36Sopenharmony_ci			drm_fb_dma_sync_non_coherent(&priv->drm, oldstate, newstate);
67262306a36Sopenharmony_ci
67362306a36Sopenharmony_ci		crtc_state = newstate->crtc->state;
67462306a36Sopenharmony_ci		plane_id = !!(priv->soc_info->has_osd && plane != &priv->f0);
67562306a36Sopenharmony_ci
67662306a36Sopenharmony_ci		addr = drm_fb_dma_get_gem_addr(newstate->fb, newstate, 0);
67762306a36Sopenharmony_ci		width = newstate->src_w >> 16;
67862306a36Sopenharmony_ci		height = newstate->src_h >> 16;
67962306a36Sopenharmony_ci		cpp = newstate->fb->format->cpp[0];
68062306a36Sopenharmony_ci
68162306a36Sopenharmony_ci		priv_state = ingenic_drm_get_new_priv_state(priv, state);
68262306a36Sopenharmony_ci		next_id = (priv_state && priv_state->use_palette) ? HWDESC_PALETTE : plane_id;
68362306a36Sopenharmony_ci
68462306a36Sopenharmony_ci		hwdesc = &priv->dma_hwdescs->hwdesc[plane_id];
68562306a36Sopenharmony_ci		hwdesc->addr = addr;
68662306a36Sopenharmony_ci		hwdesc->cmd = JZ_LCD_CMD_EOF_IRQ | (width * height * cpp / 4);
68762306a36Sopenharmony_ci		hwdesc->next = dma_hwdesc_addr(priv, next_id);
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_ci		if (priv->soc_info->use_extended_hwdesc) {
69062306a36Sopenharmony_ci			hwdesc->cmd |= JZ_LCD_CMD_FRM_ENABLE;
69162306a36Sopenharmony_ci
69262306a36Sopenharmony_ci			/* Extended 8-byte descriptor */
69362306a36Sopenharmony_ci			hwdesc->cpos = 0;
69462306a36Sopenharmony_ci			hwdesc->offsize = 0;
69562306a36Sopenharmony_ci			hwdesc->pagewidth = 0;
69662306a36Sopenharmony_ci
69762306a36Sopenharmony_ci			switch (newstate->fb->format->format) {
69862306a36Sopenharmony_ci			case DRM_FORMAT_XRGB1555:
69962306a36Sopenharmony_ci				hwdesc->cpos |= JZ_LCD_CPOS_RGB555;
70062306a36Sopenharmony_ci				fallthrough;
70162306a36Sopenharmony_ci			case DRM_FORMAT_RGB565:
70262306a36Sopenharmony_ci				hwdesc->cpos |= JZ_LCD_CPOS_BPP_15_16;
70362306a36Sopenharmony_ci				break;
70462306a36Sopenharmony_ci			case DRM_FORMAT_XRGB8888:
70562306a36Sopenharmony_ci				hwdesc->cpos |= JZ_LCD_CPOS_BPP_18_24;
70662306a36Sopenharmony_ci				break;
70762306a36Sopenharmony_ci			}
70862306a36Sopenharmony_ci			hwdesc->cpos |= (JZ_LCD_CPOS_COEFFICIENT_1 <<
70962306a36Sopenharmony_ci					 JZ_LCD_CPOS_COEFFICIENT_OFFSET);
71062306a36Sopenharmony_ci			hwdesc->dessize =
71162306a36Sopenharmony_ci				(0xff << JZ_LCD_DESSIZE_ALPHA_OFFSET) |
71262306a36Sopenharmony_ci				FIELD_PREP(JZ_LCD_DESSIZE_HEIGHT_MASK, height - 1) |
71362306a36Sopenharmony_ci				FIELD_PREP(JZ_LCD_DESSIZE_WIDTH_MASK, width - 1);
71462306a36Sopenharmony_ci		}
71562306a36Sopenharmony_ci
71662306a36Sopenharmony_ci		if (drm_atomic_crtc_needs_modeset(crtc_state)) {
71762306a36Sopenharmony_ci			fourcc = newstate->fb->format->format;
71862306a36Sopenharmony_ci
71962306a36Sopenharmony_ci			ingenic_drm_plane_config(priv->dev, plane, fourcc);
72062306a36Sopenharmony_ci
72162306a36Sopenharmony_ci			crtc_state->color_mgmt_changed = fourcc == DRM_FORMAT_C8;
72262306a36Sopenharmony_ci		}
72362306a36Sopenharmony_ci
72462306a36Sopenharmony_ci		if (crtc_state->color_mgmt_changed)
72562306a36Sopenharmony_ci			ingenic_drm_update_palette(priv, crtc_state->gamma_lut->data);
72662306a36Sopenharmony_ci	}
72762306a36Sopenharmony_ci}
72862306a36Sopenharmony_ci
72962306a36Sopenharmony_cistatic void ingenic_drm_encoder_atomic_mode_set(struct drm_encoder *encoder,
73062306a36Sopenharmony_ci						struct drm_crtc_state *crtc_state,
73162306a36Sopenharmony_ci						struct drm_connector_state *conn_state)
73262306a36Sopenharmony_ci{
73362306a36Sopenharmony_ci	struct ingenic_drm *priv = drm_device_get_priv(encoder->dev);
73462306a36Sopenharmony_ci	struct drm_display_mode *mode = &crtc_state->adjusted_mode;
73562306a36Sopenharmony_ci	struct ingenic_drm_bridge *bridge = to_ingenic_drm_bridge(encoder);
73662306a36Sopenharmony_ci	unsigned int cfg, rgbcfg = 0;
73762306a36Sopenharmony_ci
73862306a36Sopenharmony_ci	priv->panel_is_sharp = bridge->bus_cfg.flags & DRM_BUS_FLAG_SHARP_SIGNALS;
73962306a36Sopenharmony_ci
74062306a36Sopenharmony_ci	if (priv->panel_is_sharp) {
74162306a36Sopenharmony_ci		cfg = JZ_LCD_CFG_MODE_SPECIAL_TFT_1 | JZ_LCD_CFG_REV_POLARITY;
74262306a36Sopenharmony_ci	} else {
74362306a36Sopenharmony_ci		cfg = JZ_LCD_CFG_PS_DISABLE | JZ_LCD_CFG_CLS_DISABLE
74462306a36Sopenharmony_ci		    | JZ_LCD_CFG_SPL_DISABLE | JZ_LCD_CFG_REV_DISABLE;
74562306a36Sopenharmony_ci	}
74662306a36Sopenharmony_ci
74762306a36Sopenharmony_ci	if (priv->soc_info->use_extended_hwdesc)
74862306a36Sopenharmony_ci		cfg |= JZ_LCD_CFG_DESCRIPTOR_8;
74962306a36Sopenharmony_ci
75062306a36Sopenharmony_ci	if (mode->flags & DRM_MODE_FLAG_NHSYNC)
75162306a36Sopenharmony_ci		cfg |= JZ_LCD_CFG_HSYNC_ACTIVE_LOW;
75262306a36Sopenharmony_ci	if (mode->flags & DRM_MODE_FLAG_NVSYNC)
75362306a36Sopenharmony_ci		cfg |= JZ_LCD_CFG_VSYNC_ACTIVE_LOW;
75462306a36Sopenharmony_ci	if (bridge->bus_cfg.flags & DRM_BUS_FLAG_DE_LOW)
75562306a36Sopenharmony_ci		cfg |= JZ_LCD_CFG_DE_ACTIVE_LOW;
75662306a36Sopenharmony_ci	if (bridge->bus_cfg.flags & DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE)
75762306a36Sopenharmony_ci		cfg |= JZ_LCD_CFG_PCLK_FALLING_EDGE;
75862306a36Sopenharmony_ci
75962306a36Sopenharmony_ci	if (!priv->panel_is_sharp) {
76062306a36Sopenharmony_ci		if (conn_state->connector->connector_type == DRM_MODE_CONNECTOR_TV) {
76162306a36Sopenharmony_ci			if (mode->flags & DRM_MODE_FLAG_INTERLACE)
76262306a36Sopenharmony_ci				cfg |= JZ_LCD_CFG_MODE_TV_OUT_I;
76362306a36Sopenharmony_ci			else
76462306a36Sopenharmony_ci				cfg |= JZ_LCD_CFG_MODE_TV_OUT_P;
76562306a36Sopenharmony_ci		} else {
76662306a36Sopenharmony_ci			switch (bridge->bus_cfg.format) {
76762306a36Sopenharmony_ci			case MEDIA_BUS_FMT_RGB565_1X16:
76862306a36Sopenharmony_ci				cfg |= JZ_LCD_CFG_MODE_GENERIC_16BIT;
76962306a36Sopenharmony_ci				break;
77062306a36Sopenharmony_ci			case MEDIA_BUS_FMT_RGB666_1X18:
77162306a36Sopenharmony_ci				cfg |= JZ_LCD_CFG_MODE_GENERIC_18BIT;
77262306a36Sopenharmony_ci				break;
77362306a36Sopenharmony_ci			case MEDIA_BUS_FMT_RGB888_1X24:
77462306a36Sopenharmony_ci				cfg |= JZ_LCD_CFG_MODE_GENERIC_24BIT;
77562306a36Sopenharmony_ci				break;
77662306a36Sopenharmony_ci			case MEDIA_BUS_FMT_RGB888_3X8_DELTA:
77762306a36Sopenharmony_ci				rgbcfg = JZ_LCD_RGBC_EVEN_GBR | JZ_LCD_RGBC_ODD_RGB;
77862306a36Sopenharmony_ci				fallthrough;
77962306a36Sopenharmony_ci			case MEDIA_BUS_FMT_RGB888_3X8:
78062306a36Sopenharmony_ci				cfg |= JZ_LCD_CFG_MODE_8BIT_SERIAL;
78162306a36Sopenharmony_ci				break;
78262306a36Sopenharmony_ci			default:
78362306a36Sopenharmony_ci				break;
78462306a36Sopenharmony_ci			}
78562306a36Sopenharmony_ci		}
78662306a36Sopenharmony_ci	}
78762306a36Sopenharmony_ci
78862306a36Sopenharmony_ci	regmap_write(priv->map, JZ_REG_LCD_CFG, cfg);
78962306a36Sopenharmony_ci	regmap_write(priv->map, JZ_REG_LCD_RGBC, rgbcfg);
79062306a36Sopenharmony_ci}
79162306a36Sopenharmony_ci
79262306a36Sopenharmony_cistatic int ingenic_drm_bridge_attach(struct drm_bridge *bridge,
79362306a36Sopenharmony_ci				     enum drm_bridge_attach_flags flags)
79462306a36Sopenharmony_ci{
79562306a36Sopenharmony_ci	struct ingenic_drm_bridge *ib = to_ingenic_drm_bridge(bridge->encoder);
79662306a36Sopenharmony_ci
79762306a36Sopenharmony_ci	return drm_bridge_attach(bridge->encoder, ib->next_bridge,
79862306a36Sopenharmony_ci				 &ib->bridge, flags);
79962306a36Sopenharmony_ci}
80062306a36Sopenharmony_ci
80162306a36Sopenharmony_cistatic int ingenic_drm_bridge_atomic_check(struct drm_bridge *bridge,
80262306a36Sopenharmony_ci					   struct drm_bridge_state *bridge_state,
80362306a36Sopenharmony_ci					   struct drm_crtc_state *crtc_state,
80462306a36Sopenharmony_ci					   struct drm_connector_state *conn_state)
80562306a36Sopenharmony_ci{
80662306a36Sopenharmony_ci	struct drm_display_mode *mode = &crtc_state->adjusted_mode;
80762306a36Sopenharmony_ci	struct ingenic_drm_bridge *ib = to_ingenic_drm_bridge(bridge->encoder);
80862306a36Sopenharmony_ci
80962306a36Sopenharmony_ci	ib->bus_cfg = bridge_state->output_bus_cfg;
81062306a36Sopenharmony_ci
81162306a36Sopenharmony_ci	if (conn_state->connector->connector_type == DRM_MODE_CONNECTOR_TV)
81262306a36Sopenharmony_ci		return 0;
81362306a36Sopenharmony_ci
81462306a36Sopenharmony_ci	switch (bridge_state->output_bus_cfg.format) {
81562306a36Sopenharmony_ci	case MEDIA_BUS_FMT_RGB888_3X8:
81662306a36Sopenharmony_ci	case MEDIA_BUS_FMT_RGB888_3X8_DELTA:
81762306a36Sopenharmony_ci		/*
81862306a36Sopenharmony_ci		 * The LCD controller expects timing values in dot-clock ticks,
81962306a36Sopenharmony_ci		 * which is 3x the timing values in pixels when using a 3x8-bit
82062306a36Sopenharmony_ci		 * display; but it will count the display area size in pixels
82162306a36Sopenharmony_ci		 * either way. Go figure.
82262306a36Sopenharmony_ci		 */
82362306a36Sopenharmony_ci		mode->crtc_clock = mode->clock * 3;
82462306a36Sopenharmony_ci		mode->crtc_hsync_start = mode->hsync_start * 3 - mode->hdisplay * 2;
82562306a36Sopenharmony_ci		mode->crtc_hsync_end = mode->hsync_end * 3 - mode->hdisplay * 2;
82662306a36Sopenharmony_ci		mode->crtc_hdisplay = mode->hdisplay;
82762306a36Sopenharmony_ci		mode->crtc_htotal = mode->htotal * 3 - mode->hdisplay * 2;
82862306a36Sopenharmony_ci		return 0;
82962306a36Sopenharmony_ci	case MEDIA_BUS_FMT_RGB565_1X16:
83062306a36Sopenharmony_ci	case MEDIA_BUS_FMT_RGB666_1X18:
83162306a36Sopenharmony_ci	case MEDIA_BUS_FMT_RGB888_1X24:
83262306a36Sopenharmony_ci		return 0;
83362306a36Sopenharmony_ci	default:
83462306a36Sopenharmony_ci		return -EINVAL;
83562306a36Sopenharmony_ci	}
83662306a36Sopenharmony_ci}
83762306a36Sopenharmony_ci
83862306a36Sopenharmony_cistatic u32 *
83962306a36Sopenharmony_ciingenic_drm_bridge_atomic_get_input_bus_fmts(struct drm_bridge *bridge,
84062306a36Sopenharmony_ci					     struct drm_bridge_state *bridge_state,
84162306a36Sopenharmony_ci					     struct drm_crtc_state *crtc_state,
84262306a36Sopenharmony_ci					     struct drm_connector_state *conn_state,
84362306a36Sopenharmony_ci					     u32 output_fmt,
84462306a36Sopenharmony_ci					     unsigned int *num_input_fmts)
84562306a36Sopenharmony_ci{
84662306a36Sopenharmony_ci	switch (output_fmt) {
84762306a36Sopenharmony_ci	case MEDIA_BUS_FMT_RGB888_1X24:
84862306a36Sopenharmony_ci	case MEDIA_BUS_FMT_RGB666_1X18:
84962306a36Sopenharmony_ci	case MEDIA_BUS_FMT_RGB565_1X16:
85062306a36Sopenharmony_ci	case MEDIA_BUS_FMT_RGB888_3X8:
85162306a36Sopenharmony_ci	case MEDIA_BUS_FMT_RGB888_3X8_DELTA:
85262306a36Sopenharmony_ci		break;
85362306a36Sopenharmony_ci	default:
85462306a36Sopenharmony_ci		*num_input_fmts = 0;
85562306a36Sopenharmony_ci		return NULL;
85662306a36Sopenharmony_ci	}
85762306a36Sopenharmony_ci
85862306a36Sopenharmony_ci	return drm_atomic_helper_bridge_propagate_bus_fmt(bridge, bridge_state,
85962306a36Sopenharmony_ci							  crtc_state, conn_state,
86062306a36Sopenharmony_ci							  output_fmt,
86162306a36Sopenharmony_ci							  num_input_fmts);
86262306a36Sopenharmony_ci}
86362306a36Sopenharmony_ci
86462306a36Sopenharmony_cistatic irqreturn_t ingenic_drm_irq_handler(int irq, void *arg)
86562306a36Sopenharmony_ci{
86662306a36Sopenharmony_ci	struct ingenic_drm *priv = drm_device_get_priv(arg);
86762306a36Sopenharmony_ci	unsigned int state;
86862306a36Sopenharmony_ci
86962306a36Sopenharmony_ci	regmap_read(priv->map, JZ_REG_LCD_STATE, &state);
87062306a36Sopenharmony_ci
87162306a36Sopenharmony_ci	regmap_update_bits(priv->map, JZ_REG_LCD_STATE,
87262306a36Sopenharmony_ci			   JZ_LCD_STATE_EOF_IRQ, 0);
87362306a36Sopenharmony_ci
87462306a36Sopenharmony_ci	if (state & JZ_LCD_STATE_EOF_IRQ)
87562306a36Sopenharmony_ci		drm_crtc_handle_vblank(&priv->crtc);
87662306a36Sopenharmony_ci
87762306a36Sopenharmony_ci	return IRQ_HANDLED;
87862306a36Sopenharmony_ci}
87962306a36Sopenharmony_ci
88062306a36Sopenharmony_cistatic int ingenic_drm_enable_vblank(struct drm_crtc *crtc)
88162306a36Sopenharmony_ci{
88262306a36Sopenharmony_ci	struct ingenic_drm *priv = drm_crtc_get_priv(crtc);
88362306a36Sopenharmony_ci
88462306a36Sopenharmony_ci	if (priv->no_vblank)
88562306a36Sopenharmony_ci		return -EINVAL;
88662306a36Sopenharmony_ci
88762306a36Sopenharmony_ci	regmap_update_bits(priv->map, JZ_REG_LCD_CTRL,
88862306a36Sopenharmony_ci			   JZ_LCD_CTRL_EOF_IRQ, JZ_LCD_CTRL_EOF_IRQ);
88962306a36Sopenharmony_ci
89062306a36Sopenharmony_ci	return 0;
89162306a36Sopenharmony_ci}
89262306a36Sopenharmony_ci
89362306a36Sopenharmony_cistatic void ingenic_drm_disable_vblank(struct drm_crtc *crtc)
89462306a36Sopenharmony_ci{
89562306a36Sopenharmony_ci	struct ingenic_drm *priv = drm_crtc_get_priv(crtc);
89662306a36Sopenharmony_ci
89762306a36Sopenharmony_ci	regmap_update_bits(priv->map, JZ_REG_LCD_CTRL, JZ_LCD_CTRL_EOF_IRQ, 0);
89862306a36Sopenharmony_ci}
89962306a36Sopenharmony_ci
90062306a36Sopenharmony_cistatic struct drm_framebuffer *
90162306a36Sopenharmony_ciingenic_drm_gem_fb_create(struct drm_device *drm, struct drm_file *file,
90262306a36Sopenharmony_ci			  const struct drm_mode_fb_cmd2 *mode_cmd)
90362306a36Sopenharmony_ci{
90462306a36Sopenharmony_ci	struct ingenic_drm *priv = drm_device_get_priv(drm);
90562306a36Sopenharmony_ci
90662306a36Sopenharmony_ci	if (priv->soc_info->map_noncoherent)
90762306a36Sopenharmony_ci		return drm_gem_fb_create_with_dirty(drm, file, mode_cmd);
90862306a36Sopenharmony_ci
90962306a36Sopenharmony_ci	return drm_gem_fb_create(drm, file, mode_cmd);
91062306a36Sopenharmony_ci}
91162306a36Sopenharmony_ci
91262306a36Sopenharmony_cistatic struct drm_gem_object *
91362306a36Sopenharmony_ciingenic_drm_gem_create_object(struct drm_device *drm, size_t size)
91462306a36Sopenharmony_ci{
91562306a36Sopenharmony_ci	struct ingenic_drm *priv = drm_device_get_priv(drm);
91662306a36Sopenharmony_ci	struct drm_gem_dma_object *obj;
91762306a36Sopenharmony_ci
91862306a36Sopenharmony_ci	obj = kzalloc(sizeof(*obj), GFP_KERNEL);
91962306a36Sopenharmony_ci	if (!obj)
92062306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
92162306a36Sopenharmony_ci
92262306a36Sopenharmony_ci	obj->map_noncoherent = priv->soc_info->map_noncoherent;
92362306a36Sopenharmony_ci
92462306a36Sopenharmony_ci	return &obj->base;
92562306a36Sopenharmony_ci}
92662306a36Sopenharmony_ci
92762306a36Sopenharmony_cistatic struct drm_private_state *
92862306a36Sopenharmony_ciingenic_drm_duplicate_state(struct drm_private_obj *obj)
92962306a36Sopenharmony_ci{
93062306a36Sopenharmony_ci	struct ingenic_drm_private_state *state = to_ingenic_drm_priv_state(obj->state);
93162306a36Sopenharmony_ci
93262306a36Sopenharmony_ci	state = kmemdup(state, sizeof(*state), GFP_KERNEL);
93362306a36Sopenharmony_ci	if (!state)
93462306a36Sopenharmony_ci		return NULL;
93562306a36Sopenharmony_ci
93662306a36Sopenharmony_ci	__drm_atomic_helper_private_obj_duplicate_state(obj, &state->base);
93762306a36Sopenharmony_ci
93862306a36Sopenharmony_ci	return &state->base;
93962306a36Sopenharmony_ci}
94062306a36Sopenharmony_ci
94162306a36Sopenharmony_cistatic void ingenic_drm_destroy_state(struct drm_private_obj *obj,
94262306a36Sopenharmony_ci				      struct drm_private_state *state)
94362306a36Sopenharmony_ci{
94462306a36Sopenharmony_ci	struct ingenic_drm_private_state *priv_state = to_ingenic_drm_priv_state(state);
94562306a36Sopenharmony_ci
94662306a36Sopenharmony_ci	kfree(priv_state);
94762306a36Sopenharmony_ci}
94862306a36Sopenharmony_ci
94962306a36Sopenharmony_ciDEFINE_DRM_GEM_DMA_FOPS(ingenic_drm_fops);
95062306a36Sopenharmony_ci
95162306a36Sopenharmony_cistatic const struct drm_driver ingenic_drm_driver_data = {
95262306a36Sopenharmony_ci	.driver_features	= DRIVER_MODESET | DRIVER_GEM | DRIVER_ATOMIC,
95362306a36Sopenharmony_ci	.name			= "ingenic-drm",
95462306a36Sopenharmony_ci	.desc			= "DRM module for Ingenic SoCs",
95562306a36Sopenharmony_ci	.date			= "20200716",
95662306a36Sopenharmony_ci	.major			= 1,
95762306a36Sopenharmony_ci	.minor			= 1,
95862306a36Sopenharmony_ci	.patchlevel		= 0,
95962306a36Sopenharmony_ci
96062306a36Sopenharmony_ci	.fops			= &ingenic_drm_fops,
96162306a36Sopenharmony_ci	.gem_create_object	= ingenic_drm_gem_create_object,
96262306a36Sopenharmony_ci	DRM_GEM_DMA_DRIVER_OPS,
96362306a36Sopenharmony_ci};
96462306a36Sopenharmony_ci
96562306a36Sopenharmony_cistatic const struct drm_plane_funcs ingenic_drm_primary_plane_funcs = {
96662306a36Sopenharmony_ci	.update_plane		= drm_atomic_helper_update_plane,
96762306a36Sopenharmony_ci	.disable_plane		= drm_atomic_helper_disable_plane,
96862306a36Sopenharmony_ci	.reset			= drm_atomic_helper_plane_reset,
96962306a36Sopenharmony_ci	.destroy		= drm_plane_cleanup,
97062306a36Sopenharmony_ci
97162306a36Sopenharmony_ci	.atomic_duplicate_state	= drm_atomic_helper_plane_duplicate_state,
97262306a36Sopenharmony_ci	.atomic_destroy_state	= drm_atomic_helper_plane_destroy_state,
97362306a36Sopenharmony_ci};
97462306a36Sopenharmony_ci
97562306a36Sopenharmony_cistatic const struct drm_crtc_funcs ingenic_drm_crtc_funcs = {
97662306a36Sopenharmony_ci	.set_config		= drm_atomic_helper_set_config,
97762306a36Sopenharmony_ci	.page_flip		= drm_atomic_helper_page_flip,
97862306a36Sopenharmony_ci	.reset			= drm_atomic_helper_crtc_reset,
97962306a36Sopenharmony_ci	.destroy		= drm_crtc_cleanup,
98062306a36Sopenharmony_ci
98162306a36Sopenharmony_ci	.atomic_duplicate_state	= drm_atomic_helper_crtc_duplicate_state,
98262306a36Sopenharmony_ci	.atomic_destroy_state	= drm_atomic_helper_crtc_destroy_state,
98362306a36Sopenharmony_ci
98462306a36Sopenharmony_ci	.enable_vblank		= ingenic_drm_enable_vblank,
98562306a36Sopenharmony_ci	.disable_vblank		= ingenic_drm_disable_vblank,
98662306a36Sopenharmony_ci};
98762306a36Sopenharmony_ci
98862306a36Sopenharmony_cistatic const struct drm_plane_helper_funcs ingenic_drm_plane_helper_funcs = {
98962306a36Sopenharmony_ci	.atomic_update		= ingenic_drm_plane_atomic_update,
99062306a36Sopenharmony_ci	.atomic_check		= ingenic_drm_plane_atomic_check,
99162306a36Sopenharmony_ci	.atomic_disable		= ingenic_drm_plane_atomic_disable,
99262306a36Sopenharmony_ci};
99362306a36Sopenharmony_ci
99462306a36Sopenharmony_cistatic const struct drm_crtc_helper_funcs ingenic_drm_crtc_helper_funcs = {
99562306a36Sopenharmony_ci	.atomic_enable		= ingenic_drm_crtc_atomic_enable,
99662306a36Sopenharmony_ci	.atomic_disable		= ingenic_drm_crtc_atomic_disable,
99762306a36Sopenharmony_ci	.atomic_begin		= ingenic_drm_crtc_atomic_begin,
99862306a36Sopenharmony_ci	.atomic_flush		= ingenic_drm_crtc_atomic_flush,
99962306a36Sopenharmony_ci	.atomic_check		= ingenic_drm_crtc_atomic_check,
100062306a36Sopenharmony_ci	.mode_valid		= ingenic_drm_crtc_mode_valid,
100162306a36Sopenharmony_ci};
100262306a36Sopenharmony_ci
100362306a36Sopenharmony_cistatic const struct drm_encoder_helper_funcs ingenic_drm_encoder_helper_funcs = {
100462306a36Sopenharmony_ci	.atomic_mode_set        = ingenic_drm_encoder_atomic_mode_set,
100562306a36Sopenharmony_ci};
100662306a36Sopenharmony_ci
100762306a36Sopenharmony_cistatic const struct drm_bridge_funcs ingenic_drm_bridge_funcs = {
100862306a36Sopenharmony_ci	.attach			= ingenic_drm_bridge_attach,
100962306a36Sopenharmony_ci	.atomic_enable		= ingenic_drm_bridge_atomic_enable,
101062306a36Sopenharmony_ci	.atomic_disable		= ingenic_drm_bridge_atomic_disable,
101162306a36Sopenharmony_ci	.atomic_check		= ingenic_drm_bridge_atomic_check,
101262306a36Sopenharmony_ci	.atomic_reset		= drm_atomic_helper_bridge_reset,
101362306a36Sopenharmony_ci	.atomic_duplicate_state	= drm_atomic_helper_bridge_duplicate_state,
101462306a36Sopenharmony_ci	.atomic_destroy_state	= drm_atomic_helper_bridge_destroy_state,
101562306a36Sopenharmony_ci	.atomic_get_input_bus_fmts = ingenic_drm_bridge_atomic_get_input_bus_fmts,
101662306a36Sopenharmony_ci};
101762306a36Sopenharmony_ci
101862306a36Sopenharmony_cistatic const struct drm_mode_config_funcs ingenic_drm_mode_config_funcs = {
101962306a36Sopenharmony_ci	.fb_create		= ingenic_drm_gem_fb_create,
102062306a36Sopenharmony_ci	.atomic_check		= drm_atomic_helper_check,
102162306a36Sopenharmony_ci	.atomic_commit		= drm_atomic_helper_commit,
102262306a36Sopenharmony_ci};
102362306a36Sopenharmony_ci
102462306a36Sopenharmony_cistatic struct drm_mode_config_helper_funcs ingenic_drm_mode_config_helpers = {
102562306a36Sopenharmony_ci	.atomic_commit_tail = drm_atomic_helper_commit_tail,
102662306a36Sopenharmony_ci};
102762306a36Sopenharmony_ci
102862306a36Sopenharmony_cistatic const struct drm_private_state_funcs ingenic_drm_private_state_funcs = {
102962306a36Sopenharmony_ci	.atomic_duplicate_state = ingenic_drm_duplicate_state,
103062306a36Sopenharmony_ci	.atomic_destroy_state = ingenic_drm_destroy_state,
103162306a36Sopenharmony_ci};
103262306a36Sopenharmony_ci
103362306a36Sopenharmony_cistatic void ingenic_drm_unbind_all(void *d)
103462306a36Sopenharmony_ci{
103562306a36Sopenharmony_ci	struct ingenic_drm *priv = d;
103662306a36Sopenharmony_ci
103762306a36Sopenharmony_ci	component_unbind_all(priv->dev, &priv->drm);
103862306a36Sopenharmony_ci}
103962306a36Sopenharmony_ci
104062306a36Sopenharmony_cistatic void __maybe_unused ingenic_drm_release_rmem(void *d)
104162306a36Sopenharmony_ci{
104262306a36Sopenharmony_ci	of_reserved_mem_device_release(d);
104362306a36Sopenharmony_ci}
104462306a36Sopenharmony_ci
104562306a36Sopenharmony_cistatic void ingenic_drm_configure_hwdesc(struct ingenic_drm *priv,
104662306a36Sopenharmony_ci					 unsigned int hwdesc,
104762306a36Sopenharmony_ci					 unsigned int next_hwdesc, u32 id)
104862306a36Sopenharmony_ci{
104962306a36Sopenharmony_ci	struct ingenic_dma_hwdesc *desc = &priv->dma_hwdescs->hwdesc[hwdesc];
105062306a36Sopenharmony_ci
105162306a36Sopenharmony_ci	desc->next = dma_hwdesc_addr(priv, next_hwdesc);
105262306a36Sopenharmony_ci	desc->id = id;
105362306a36Sopenharmony_ci}
105462306a36Sopenharmony_ci
105562306a36Sopenharmony_cistatic void ingenic_drm_configure_hwdesc_palette(struct ingenic_drm *priv)
105662306a36Sopenharmony_ci{
105762306a36Sopenharmony_ci	struct ingenic_dma_hwdesc *desc;
105862306a36Sopenharmony_ci
105962306a36Sopenharmony_ci	ingenic_drm_configure_hwdesc(priv, HWDESC_PALETTE, 0, 0xc0);
106062306a36Sopenharmony_ci
106162306a36Sopenharmony_ci	desc = &priv->dma_hwdescs->hwdesc[HWDESC_PALETTE];
106262306a36Sopenharmony_ci	desc->addr = priv->dma_hwdescs_phys
106362306a36Sopenharmony_ci		+ offsetof(struct ingenic_dma_hwdescs, palette);
106462306a36Sopenharmony_ci	desc->cmd = JZ_LCD_CMD_ENABLE_PAL
106562306a36Sopenharmony_ci		| (sizeof(priv->dma_hwdescs->palette) / 4);
106662306a36Sopenharmony_ci}
106762306a36Sopenharmony_ci
106862306a36Sopenharmony_cistatic void ingenic_drm_configure_hwdesc_plane(struct ingenic_drm *priv,
106962306a36Sopenharmony_ci					       unsigned int plane)
107062306a36Sopenharmony_ci{
107162306a36Sopenharmony_ci	ingenic_drm_configure_hwdesc(priv, plane, plane, 0xf0 | plane);
107262306a36Sopenharmony_ci}
107362306a36Sopenharmony_ci
107462306a36Sopenharmony_cistatic void ingenic_drm_atomic_private_obj_fini(struct drm_device *drm, void *private_obj)
107562306a36Sopenharmony_ci{
107662306a36Sopenharmony_ci	drm_atomic_private_obj_fini(private_obj);
107762306a36Sopenharmony_ci}
107862306a36Sopenharmony_ci
107962306a36Sopenharmony_cistatic int ingenic_drm_bind(struct device *dev, bool has_components)
108062306a36Sopenharmony_ci{
108162306a36Sopenharmony_ci	struct platform_device *pdev = to_platform_device(dev);
108262306a36Sopenharmony_ci	struct ingenic_drm_private_state *private_state;
108362306a36Sopenharmony_ci	const struct jz_soc_info *soc_info;
108462306a36Sopenharmony_ci	struct ingenic_drm *priv;
108562306a36Sopenharmony_ci	struct clk *parent_clk;
108662306a36Sopenharmony_ci	struct drm_plane *primary;
108762306a36Sopenharmony_ci	struct drm_bridge *bridge;
108862306a36Sopenharmony_ci	struct drm_panel *panel;
108962306a36Sopenharmony_ci	struct drm_connector *connector;
109062306a36Sopenharmony_ci	struct drm_encoder *encoder;
109162306a36Sopenharmony_ci	struct ingenic_drm_bridge *ib;
109262306a36Sopenharmony_ci	struct drm_device *drm;
109362306a36Sopenharmony_ci	void __iomem *base;
109462306a36Sopenharmony_ci	struct resource *res;
109562306a36Sopenharmony_ci	struct regmap_config regmap_config;
109662306a36Sopenharmony_ci	long parent_rate;
109762306a36Sopenharmony_ci	unsigned int i, clone_mask = 0;
109862306a36Sopenharmony_ci	int ret, irq;
109962306a36Sopenharmony_ci	u32 osdc = 0;
110062306a36Sopenharmony_ci
110162306a36Sopenharmony_ci	soc_info = of_device_get_match_data(dev);
110262306a36Sopenharmony_ci	if (!soc_info) {
110362306a36Sopenharmony_ci		dev_err(dev, "Missing platform data\n");
110462306a36Sopenharmony_ci		return -EINVAL;
110562306a36Sopenharmony_ci	}
110662306a36Sopenharmony_ci
110762306a36Sopenharmony_ci	if (IS_ENABLED(CONFIG_OF_RESERVED_MEM)) {
110862306a36Sopenharmony_ci		ret = of_reserved_mem_device_init(dev);
110962306a36Sopenharmony_ci
111062306a36Sopenharmony_ci		if (ret && ret != -ENODEV)
111162306a36Sopenharmony_ci			dev_warn(dev, "Failed to get reserved memory: %d\n", ret);
111262306a36Sopenharmony_ci
111362306a36Sopenharmony_ci		if (!ret) {
111462306a36Sopenharmony_ci			ret = devm_add_action_or_reset(dev, ingenic_drm_release_rmem, dev);
111562306a36Sopenharmony_ci			if (ret)
111662306a36Sopenharmony_ci				return ret;
111762306a36Sopenharmony_ci		}
111862306a36Sopenharmony_ci	}
111962306a36Sopenharmony_ci
112062306a36Sopenharmony_ci	priv = devm_drm_dev_alloc(dev, &ingenic_drm_driver_data,
112162306a36Sopenharmony_ci				  struct ingenic_drm, drm);
112262306a36Sopenharmony_ci	if (IS_ERR(priv))
112362306a36Sopenharmony_ci		return PTR_ERR(priv);
112462306a36Sopenharmony_ci
112562306a36Sopenharmony_ci	priv->soc_info = soc_info;
112662306a36Sopenharmony_ci	priv->dev = dev;
112762306a36Sopenharmony_ci	drm = &priv->drm;
112862306a36Sopenharmony_ci
112962306a36Sopenharmony_ci	platform_set_drvdata(pdev, priv);
113062306a36Sopenharmony_ci
113162306a36Sopenharmony_ci	ret = drmm_mode_config_init(drm);
113262306a36Sopenharmony_ci	if (ret)
113362306a36Sopenharmony_ci		return ret;
113462306a36Sopenharmony_ci
113562306a36Sopenharmony_ci	drm->mode_config.min_width = 0;
113662306a36Sopenharmony_ci	drm->mode_config.min_height = 0;
113762306a36Sopenharmony_ci	drm->mode_config.max_width = soc_info->max_width;
113862306a36Sopenharmony_ci	drm->mode_config.max_height = 4095;
113962306a36Sopenharmony_ci	drm->mode_config.funcs = &ingenic_drm_mode_config_funcs;
114062306a36Sopenharmony_ci	drm->mode_config.helper_private = &ingenic_drm_mode_config_helpers;
114162306a36Sopenharmony_ci
114262306a36Sopenharmony_ci	base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
114362306a36Sopenharmony_ci	if (IS_ERR(base)) {
114462306a36Sopenharmony_ci		dev_err(dev, "Failed to get memory resource\n");
114562306a36Sopenharmony_ci		return PTR_ERR(base);
114662306a36Sopenharmony_ci	}
114762306a36Sopenharmony_ci
114862306a36Sopenharmony_ci	regmap_config = ingenic_drm_regmap_config;
114962306a36Sopenharmony_ci	regmap_config.max_register = res->end - res->start;
115062306a36Sopenharmony_ci	priv->map = devm_regmap_init_mmio(dev, base,
115162306a36Sopenharmony_ci					  &regmap_config);
115262306a36Sopenharmony_ci	if (IS_ERR(priv->map)) {
115362306a36Sopenharmony_ci		dev_err(dev, "Failed to create regmap\n");
115462306a36Sopenharmony_ci		return PTR_ERR(priv->map);
115562306a36Sopenharmony_ci	}
115662306a36Sopenharmony_ci
115762306a36Sopenharmony_ci	irq = platform_get_irq(pdev, 0);
115862306a36Sopenharmony_ci	if (irq < 0)
115962306a36Sopenharmony_ci		return irq;
116062306a36Sopenharmony_ci
116162306a36Sopenharmony_ci	if (soc_info->needs_dev_clk) {
116262306a36Sopenharmony_ci		priv->lcd_clk = devm_clk_get(dev, "lcd");
116362306a36Sopenharmony_ci		if (IS_ERR(priv->lcd_clk)) {
116462306a36Sopenharmony_ci			dev_err(dev, "Failed to get lcd clock\n");
116562306a36Sopenharmony_ci			return PTR_ERR(priv->lcd_clk);
116662306a36Sopenharmony_ci		}
116762306a36Sopenharmony_ci	}
116862306a36Sopenharmony_ci
116962306a36Sopenharmony_ci	priv->pix_clk = devm_clk_get(dev, "lcd_pclk");
117062306a36Sopenharmony_ci	if (IS_ERR(priv->pix_clk)) {
117162306a36Sopenharmony_ci		dev_err(dev, "Failed to get pixel clock\n");
117262306a36Sopenharmony_ci		return PTR_ERR(priv->pix_clk);
117362306a36Sopenharmony_ci	}
117462306a36Sopenharmony_ci
117562306a36Sopenharmony_ci	priv->dma_hwdescs = dmam_alloc_coherent(dev,
117662306a36Sopenharmony_ci						sizeof(*priv->dma_hwdescs),
117762306a36Sopenharmony_ci						&priv->dma_hwdescs_phys,
117862306a36Sopenharmony_ci						GFP_KERNEL);
117962306a36Sopenharmony_ci	if (!priv->dma_hwdescs)
118062306a36Sopenharmony_ci		return -ENOMEM;
118162306a36Sopenharmony_ci
118262306a36Sopenharmony_ci	/* Configure DMA hwdesc for foreground0 plane */
118362306a36Sopenharmony_ci	ingenic_drm_configure_hwdesc_plane(priv, 0);
118462306a36Sopenharmony_ci
118562306a36Sopenharmony_ci	/* Configure DMA hwdesc for foreground1 plane */
118662306a36Sopenharmony_ci	ingenic_drm_configure_hwdesc_plane(priv, 1);
118762306a36Sopenharmony_ci
118862306a36Sopenharmony_ci	/* Configure DMA hwdesc for palette */
118962306a36Sopenharmony_ci	ingenic_drm_configure_hwdesc_palette(priv);
119062306a36Sopenharmony_ci
119162306a36Sopenharmony_ci	primary = priv->soc_info->has_osd ? &priv->f1 : &priv->f0;
119262306a36Sopenharmony_ci
119362306a36Sopenharmony_ci	drm_plane_helper_add(primary, &ingenic_drm_plane_helper_funcs);
119462306a36Sopenharmony_ci
119562306a36Sopenharmony_ci	ret = drm_universal_plane_init(drm, primary, 1,
119662306a36Sopenharmony_ci				       &ingenic_drm_primary_plane_funcs,
119762306a36Sopenharmony_ci				       priv->soc_info->formats_f1,
119862306a36Sopenharmony_ci				       priv->soc_info->num_formats_f1,
119962306a36Sopenharmony_ci				       NULL, DRM_PLANE_TYPE_PRIMARY, NULL);
120062306a36Sopenharmony_ci	if (ret) {
120162306a36Sopenharmony_ci		dev_err(dev, "Failed to register plane: %i\n", ret);
120262306a36Sopenharmony_ci		return ret;
120362306a36Sopenharmony_ci	}
120462306a36Sopenharmony_ci
120562306a36Sopenharmony_ci	if (soc_info->map_noncoherent)
120662306a36Sopenharmony_ci		drm_plane_enable_fb_damage_clips(&priv->f1);
120762306a36Sopenharmony_ci
120862306a36Sopenharmony_ci	drm_crtc_helper_add(&priv->crtc, &ingenic_drm_crtc_helper_funcs);
120962306a36Sopenharmony_ci
121062306a36Sopenharmony_ci	ret = drm_crtc_init_with_planes(drm, &priv->crtc, primary,
121162306a36Sopenharmony_ci					NULL, &ingenic_drm_crtc_funcs, NULL);
121262306a36Sopenharmony_ci	if (ret) {
121362306a36Sopenharmony_ci		dev_err(dev, "Failed to init CRTC: %i\n", ret);
121462306a36Sopenharmony_ci		return ret;
121562306a36Sopenharmony_ci	}
121662306a36Sopenharmony_ci
121762306a36Sopenharmony_ci	drm_crtc_enable_color_mgmt(&priv->crtc, 0, false,
121862306a36Sopenharmony_ci				   ARRAY_SIZE(priv->dma_hwdescs->palette));
121962306a36Sopenharmony_ci
122062306a36Sopenharmony_ci	if (soc_info->has_osd) {
122162306a36Sopenharmony_ci		drm_plane_helper_add(&priv->f0,
122262306a36Sopenharmony_ci				     &ingenic_drm_plane_helper_funcs);
122362306a36Sopenharmony_ci
122462306a36Sopenharmony_ci		ret = drm_universal_plane_init(drm, &priv->f0, 1,
122562306a36Sopenharmony_ci					       &ingenic_drm_primary_plane_funcs,
122662306a36Sopenharmony_ci					       priv->soc_info->formats_f0,
122762306a36Sopenharmony_ci					       priv->soc_info->num_formats_f0,
122862306a36Sopenharmony_ci					       NULL, DRM_PLANE_TYPE_OVERLAY,
122962306a36Sopenharmony_ci					       NULL);
123062306a36Sopenharmony_ci		if (ret) {
123162306a36Sopenharmony_ci			dev_err(dev, "Failed to register overlay plane: %i\n",
123262306a36Sopenharmony_ci				ret);
123362306a36Sopenharmony_ci			return ret;
123462306a36Sopenharmony_ci		}
123562306a36Sopenharmony_ci
123662306a36Sopenharmony_ci		if (soc_info->map_noncoherent)
123762306a36Sopenharmony_ci			drm_plane_enable_fb_damage_clips(&priv->f0);
123862306a36Sopenharmony_ci
123962306a36Sopenharmony_ci		if (IS_ENABLED(CONFIG_DRM_INGENIC_IPU) && has_components) {
124062306a36Sopenharmony_ci			ret = component_bind_all(dev, drm);
124162306a36Sopenharmony_ci			if (ret) {
124262306a36Sopenharmony_ci				if (ret != -EPROBE_DEFER)
124362306a36Sopenharmony_ci					dev_err(dev, "Failed to bind components: %i\n", ret);
124462306a36Sopenharmony_ci				return ret;
124562306a36Sopenharmony_ci			}
124662306a36Sopenharmony_ci
124762306a36Sopenharmony_ci			ret = devm_add_action_or_reset(dev, ingenic_drm_unbind_all, priv);
124862306a36Sopenharmony_ci			if (ret)
124962306a36Sopenharmony_ci				return ret;
125062306a36Sopenharmony_ci
125162306a36Sopenharmony_ci			priv->ipu_plane = drm_plane_from_index(drm, 2);
125262306a36Sopenharmony_ci			if (!priv->ipu_plane) {
125362306a36Sopenharmony_ci				dev_err(dev, "Failed to retrieve IPU plane\n");
125462306a36Sopenharmony_ci				return -EINVAL;
125562306a36Sopenharmony_ci			}
125662306a36Sopenharmony_ci		}
125762306a36Sopenharmony_ci	}
125862306a36Sopenharmony_ci
125962306a36Sopenharmony_ci	for (i = 0; ; i++) {
126062306a36Sopenharmony_ci		ret = drm_of_find_panel_or_bridge(dev->of_node, 0, i, &panel, &bridge);
126162306a36Sopenharmony_ci		if (ret) {
126262306a36Sopenharmony_ci			if (ret == -ENODEV)
126362306a36Sopenharmony_ci				break; /* we're done */
126462306a36Sopenharmony_ci			if (ret != -EPROBE_DEFER)
126562306a36Sopenharmony_ci				dev_err(dev, "Failed to get bridge handle\n");
126662306a36Sopenharmony_ci			return ret;
126762306a36Sopenharmony_ci		}
126862306a36Sopenharmony_ci
126962306a36Sopenharmony_ci		if (panel)
127062306a36Sopenharmony_ci			bridge = devm_drm_panel_bridge_add_typed(dev, panel,
127162306a36Sopenharmony_ci								 DRM_MODE_CONNECTOR_DPI);
127262306a36Sopenharmony_ci
127362306a36Sopenharmony_ci		ib = drmm_encoder_alloc(drm, struct ingenic_drm_bridge, encoder,
127462306a36Sopenharmony_ci					NULL, DRM_MODE_ENCODER_DPI, NULL);
127562306a36Sopenharmony_ci		if (IS_ERR(ib)) {
127662306a36Sopenharmony_ci			ret = PTR_ERR(ib);
127762306a36Sopenharmony_ci			dev_err(dev, "Failed to init encoder: %d\n", ret);
127862306a36Sopenharmony_ci			return ret;
127962306a36Sopenharmony_ci		}
128062306a36Sopenharmony_ci
128162306a36Sopenharmony_ci		encoder = &ib->encoder;
128262306a36Sopenharmony_ci		encoder->possible_crtcs = drm_crtc_mask(&priv->crtc);
128362306a36Sopenharmony_ci
128462306a36Sopenharmony_ci		drm_encoder_helper_add(encoder, &ingenic_drm_encoder_helper_funcs);
128562306a36Sopenharmony_ci
128662306a36Sopenharmony_ci		ib->bridge.funcs = &ingenic_drm_bridge_funcs;
128762306a36Sopenharmony_ci		ib->next_bridge = bridge;
128862306a36Sopenharmony_ci
128962306a36Sopenharmony_ci		ret = drm_bridge_attach(encoder, &ib->bridge, NULL,
129062306a36Sopenharmony_ci					DRM_BRIDGE_ATTACH_NO_CONNECTOR);
129162306a36Sopenharmony_ci		if (ret) {
129262306a36Sopenharmony_ci			dev_err(dev, "Unable to attach bridge\n");
129362306a36Sopenharmony_ci			return ret;
129462306a36Sopenharmony_ci		}
129562306a36Sopenharmony_ci
129662306a36Sopenharmony_ci		connector = drm_bridge_connector_init(drm, encoder);
129762306a36Sopenharmony_ci		if (IS_ERR(connector)) {
129862306a36Sopenharmony_ci			dev_err(dev, "Unable to init connector\n");
129962306a36Sopenharmony_ci			return PTR_ERR(connector);
130062306a36Sopenharmony_ci		}
130162306a36Sopenharmony_ci
130262306a36Sopenharmony_ci		drm_connector_attach_encoder(connector, encoder);
130362306a36Sopenharmony_ci	}
130462306a36Sopenharmony_ci
130562306a36Sopenharmony_ci	drm_for_each_encoder(encoder, drm) {
130662306a36Sopenharmony_ci		clone_mask |= BIT(drm_encoder_index(encoder));
130762306a36Sopenharmony_ci	}
130862306a36Sopenharmony_ci
130962306a36Sopenharmony_ci	drm_for_each_encoder(encoder, drm) {
131062306a36Sopenharmony_ci		encoder->possible_clones = clone_mask;
131162306a36Sopenharmony_ci	}
131262306a36Sopenharmony_ci
131362306a36Sopenharmony_ci	ret = devm_request_irq(dev, irq, ingenic_drm_irq_handler, 0, drm->driver->name, drm);
131462306a36Sopenharmony_ci	if (ret) {
131562306a36Sopenharmony_ci		dev_err(dev, "Unable to install IRQ handler\n");
131662306a36Sopenharmony_ci		return ret;
131762306a36Sopenharmony_ci	}
131862306a36Sopenharmony_ci
131962306a36Sopenharmony_ci	ret = drm_vblank_init(drm, 1);
132062306a36Sopenharmony_ci	if (ret) {
132162306a36Sopenharmony_ci		dev_err(dev, "Failed calling drm_vblank_init()\n");
132262306a36Sopenharmony_ci		return ret;
132362306a36Sopenharmony_ci	}
132462306a36Sopenharmony_ci
132562306a36Sopenharmony_ci	drm_mode_config_reset(drm);
132662306a36Sopenharmony_ci
132762306a36Sopenharmony_ci	ret = clk_prepare_enable(priv->pix_clk);
132862306a36Sopenharmony_ci	if (ret) {
132962306a36Sopenharmony_ci		dev_err(dev, "Unable to start pixel clock\n");
133062306a36Sopenharmony_ci		return ret;
133162306a36Sopenharmony_ci	}
133262306a36Sopenharmony_ci
133362306a36Sopenharmony_ci	if (priv->lcd_clk) {
133462306a36Sopenharmony_ci		parent_clk = clk_get_parent(priv->lcd_clk);
133562306a36Sopenharmony_ci		parent_rate = clk_get_rate(parent_clk);
133662306a36Sopenharmony_ci
133762306a36Sopenharmony_ci		/* LCD Device clock must be 3x the pixel clock for STN panels,
133862306a36Sopenharmony_ci		 * or 1.5x the pixel clock for TFT panels. To avoid having to
133962306a36Sopenharmony_ci		 * check for the LCD device clock everytime we do a mode change,
134062306a36Sopenharmony_ci		 * we set the LCD device clock to the highest rate possible.
134162306a36Sopenharmony_ci		 */
134262306a36Sopenharmony_ci		ret = clk_set_rate(priv->lcd_clk, parent_rate);
134362306a36Sopenharmony_ci		if (ret) {
134462306a36Sopenharmony_ci			dev_err(dev, "Unable to set LCD clock rate\n");
134562306a36Sopenharmony_ci			goto err_pixclk_disable;
134662306a36Sopenharmony_ci		}
134762306a36Sopenharmony_ci
134862306a36Sopenharmony_ci		ret = clk_prepare_enable(priv->lcd_clk);
134962306a36Sopenharmony_ci		if (ret) {
135062306a36Sopenharmony_ci			dev_err(dev, "Unable to start lcd clock\n");
135162306a36Sopenharmony_ci			goto err_pixclk_disable;
135262306a36Sopenharmony_ci		}
135362306a36Sopenharmony_ci	}
135462306a36Sopenharmony_ci
135562306a36Sopenharmony_ci	/* Enable OSD if available */
135662306a36Sopenharmony_ci	if (soc_info->has_osd)
135762306a36Sopenharmony_ci		osdc |= JZ_LCD_OSDC_OSDEN;
135862306a36Sopenharmony_ci	if (soc_info->has_alpha)
135962306a36Sopenharmony_ci		osdc |= JZ_LCD_OSDC_ALPHAEN;
136062306a36Sopenharmony_ci	regmap_write(priv->map, JZ_REG_LCD_OSDC, osdc);
136162306a36Sopenharmony_ci
136262306a36Sopenharmony_ci	mutex_init(&priv->clk_mutex);
136362306a36Sopenharmony_ci	priv->clock_nb.notifier_call = ingenic_drm_update_pixclk;
136462306a36Sopenharmony_ci
136562306a36Sopenharmony_ci	parent_clk = clk_get_parent(priv->pix_clk);
136662306a36Sopenharmony_ci	ret = clk_notifier_register(parent_clk, &priv->clock_nb);
136762306a36Sopenharmony_ci	if (ret) {
136862306a36Sopenharmony_ci		dev_err(dev, "Unable to register clock notifier\n");
136962306a36Sopenharmony_ci		goto err_devclk_disable;
137062306a36Sopenharmony_ci	}
137162306a36Sopenharmony_ci
137262306a36Sopenharmony_ci	private_state = kzalloc(sizeof(*private_state), GFP_KERNEL);
137362306a36Sopenharmony_ci	if (!private_state) {
137462306a36Sopenharmony_ci		ret = -ENOMEM;
137562306a36Sopenharmony_ci		goto err_clk_notifier_unregister;
137662306a36Sopenharmony_ci	}
137762306a36Sopenharmony_ci
137862306a36Sopenharmony_ci	drm_atomic_private_obj_init(drm, &priv->private_obj, &private_state->base,
137962306a36Sopenharmony_ci				    &ingenic_drm_private_state_funcs);
138062306a36Sopenharmony_ci
138162306a36Sopenharmony_ci	ret = drmm_add_action_or_reset(drm, ingenic_drm_atomic_private_obj_fini,
138262306a36Sopenharmony_ci				       &priv->private_obj);
138362306a36Sopenharmony_ci	if (ret)
138462306a36Sopenharmony_ci		goto err_private_state_free;
138562306a36Sopenharmony_ci
138662306a36Sopenharmony_ci	ret = drm_dev_register(drm, 0);
138762306a36Sopenharmony_ci	if (ret) {
138862306a36Sopenharmony_ci		dev_err(dev, "Failed to register DRM driver\n");
138962306a36Sopenharmony_ci		goto err_clk_notifier_unregister;
139062306a36Sopenharmony_ci	}
139162306a36Sopenharmony_ci
139262306a36Sopenharmony_ci	drm_fbdev_generic_setup(drm, 32);
139362306a36Sopenharmony_ci
139462306a36Sopenharmony_ci	return 0;
139562306a36Sopenharmony_ci
139662306a36Sopenharmony_cierr_private_state_free:
139762306a36Sopenharmony_ci	kfree(private_state);
139862306a36Sopenharmony_cierr_clk_notifier_unregister:
139962306a36Sopenharmony_ci	clk_notifier_unregister(parent_clk, &priv->clock_nb);
140062306a36Sopenharmony_cierr_devclk_disable:
140162306a36Sopenharmony_ci	if (priv->lcd_clk)
140262306a36Sopenharmony_ci		clk_disable_unprepare(priv->lcd_clk);
140362306a36Sopenharmony_cierr_pixclk_disable:
140462306a36Sopenharmony_ci	clk_disable_unprepare(priv->pix_clk);
140562306a36Sopenharmony_ci	return ret;
140662306a36Sopenharmony_ci}
140762306a36Sopenharmony_ci
140862306a36Sopenharmony_cistatic int ingenic_drm_bind_with_components(struct device *dev)
140962306a36Sopenharmony_ci{
141062306a36Sopenharmony_ci	return ingenic_drm_bind(dev, true);
141162306a36Sopenharmony_ci}
141262306a36Sopenharmony_ci
141362306a36Sopenharmony_cistatic void ingenic_drm_unbind(struct device *dev)
141462306a36Sopenharmony_ci{
141562306a36Sopenharmony_ci	struct ingenic_drm *priv = dev_get_drvdata(dev);
141662306a36Sopenharmony_ci	struct clk *parent_clk = clk_get_parent(priv->pix_clk);
141762306a36Sopenharmony_ci
141862306a36Sopenharmony_ci	clk_notifier_unregister(parent_clk, &priv->clock_nb);
141962306a36Sopenharmony_ci	if (priv->lcd_clk)
142062306a36Sopenharmony_ci		clk_disable_unprepare(priv->lcd_clk);
142162306a36Sopenharmony_ci	clk_disable_unprepare(priv->pix_clk);
142262306a36Sopenharmony_ci
142362306a36Sopenharmony_ci	drm_dev_unregister(&priv->drm);
142462306a36Sopenharmony_ci	drm_atomic_helper_shutdown(&priv->drm);
142562306a36Sopenharmony_ci}
142662306a36Sopenharmony_ci
142762306a36Sopenharmony_cistatic const struct component_master_ops ingenic_master_ops = {
142862306a36Sopenharmony_ci	.bind = ingenic_drm_bind_with_components,
142962306a36Sopenharmony_ci	.unbind = ingenic_drm_unbind,
143062306a36Sopenharmony_ci};
143162306a36Sopenharmony_ci
143262306a36Sopenharmony_cistatic int ingenic_drm_probe(struct platform_device *pdev)
143362306a36Sopenharmony_ci{
143462306a36Sopenharmony_ci	struct device *dev = &pdev->dev;
143562306a36Sopenharmony_ci	struct component_match *match = NULL;
143662306a36Sopenharmony_ci	struct device_node *np;
143762306a36Sopenharmony_ci
143862306a36Sopenharmony_ci	if (!IS_ENABLED(CONFIG_DRM_INGENIC_IPU))
143962306a36Sopenharmony_ci		return ingenic_drm_bind(dev, false);
144062306a36Sopenharmony_ci
144162306a36Sopenharmony_ci	/* IPU is at port address 8 */
144262306a36Sopenharmony_ci	np = of_graph_get_remote_node(dev->of_node, 8, 0);
144362306a36Sopenharmony_ci	if (!np)
144462306a36Sopenharmony_ci		return ingenic_drm_bind(dev, false);
144562306a36Sopenharmony_ci
144662306a36Sopenharmony_ci	drm_of_component_match_add(dev, &match, component_compare_of, np);
144762306a36Sopenharmony_ci	of_node_put(np);
144862306a36Sopenharmony_ci
144962306a36Sopenharmony_ci	return component_master_add_with_match(dev, &ingenic_master_ops, match);
145062306a36Sopenharmony_ci}
145162306a36Sopenharmony_ci
145262306a36Sopenharmony_cistatic int ingenic_drm_remove(struct platform_device *pdev)
145362306a36Sopenharmony_ci{
145462306a36Sopenharmony_ci	struct device *dev = &pdev->dev;
145562306a36Sopenharmony_ci
145662306a36Sopenharmony_ci	if (!IS_ENABLED(CONFIG_DRM_INGENIC_IPU))
145762306a36Sopenharmony_ci		ingenic_drm_unbind(dev);
145862306a36Sopenharmony_ci	else
145962306a36Sopenharmony_ci		component_master_del(dev, &ingenic_master_ops);
146062306a36Sopenharmony_ci
146162306a36Sopenharmony_ci	return 0;
146262306a36Sopenharmony_ci}
146362306a36Sopenharmony_ci
146462306a36Sopenharmony_cistatic int ingenic_drm_suspend(struct device *dev)
146562306a36Sopenharmony_ci{
146662306a36Sopenharmony_ci	struct ingenic_drm *priv = dev_get_drvdata(dev);
146762306a36Sopenharmony_ci
146862306a36Sopenharmony_ci	return drm_mode_config_helper_suspend(&priv->drm);
146962306a36Sopenharmony_ci}
147062306a36Sopenharmony_ci
147162306a36Sopenharmony_cistatic int ingenic_drm_resume(struct device *dev)
147262306a36Sopenharmony_ci{
147362306a36Sopenharmony_ci	struct ingenic_drm *priv = dev_get_drvdata(dev);
147462306a36Sopenharmony_ci
147562306a36Sopenharmony_ci	return drm_mode_config_helper_resume(&priv->drm);
147662306a36Sopenharmony_ci}
147762306a36Sopenharmony_ci
147862306a36Sopenharmony_cistatic DEFINE_SIMPLE_DEV_PM_OPS(ingenic_drm_pm_ops,
147962306a36Sopenharmony_ci				ingenic_drm_suspend, ingenic_drm_resume);
148062306a36Sopenharmony_ci
148162306a36Sopenharmony_cistatic const u32 jz4740_formats[] = {
148262306a36Sopenharmony_ci	DRM_FORMAT_XRGB1555,
148362306a36Sopenharmony_ci	DRM_FORMAT_RGB565,
148462306a36Sopenharmony_ci	DRM_FORMAT_XRGB8888,
148562306a36Sopenharmony_ci};
148662306a36Sopenharmony_ci
148762306a36Sopenharmony_cistatic const u32 jz4725b_formats_f1[] = {
148862306a36Sopenharmony_ci	DRM_FORMAT_XRGB1555,
148962306a36Sopenharmony_ci	DRM_FORMAT_RGB565,
149062306a36Sopenharmony_ci	DRM_FORMAT_XRGB8888,
149162306a36Sopenharmony_ci};
149262306a36Sopenharmony_ci
149362306a36Sopenharmony_cistatic const u32 jz4725b_formats_f0[] = {
149462306a36Sopenharmony_ci	DRM_FORMAT_C8,
149562306a36Sopenharmony_ci	DRM_FORMAT_XRGB1555,
149662306a36Sopenharmony_ci	DRM_FORMAT_RGB565,
149762306a36Sopenharmony_ci	DRM_FORMAT_XRGB8888,
149862306a36Sopenharmony_ci};
149962306a36Sopenharmony_ci
150062306a36Sopenharmony_cistatic const u32 jz4770_formats_f1[] = {
150162306a36Sopenharmony_ci	DRM_FORMAT_XRGB1555,
150262306a36Sopenharmony_ci	DRM_FORMAT_RGB565,
150362306a36Sopenharmony_ci	DRM_FORMAT_RGB888,
150462306a36Sopenharmony_ci	DRM_FORMAT_XRGB8888,
150562306a36Sopenharmony_ci	DRM_FORMAT_XRGB2101010,
150662306a36Sopenharmony_ci};
150762306a36Sopenharmony_ci
150862306a36Sopenharmony_cistatic const u32 jz4770_formats_f0[] = {
150962306a36Sopenharmony_ci	DRM_FORMAT_C8,
151062306a36Sopenharmony_ci	DRM_FORMAT_XRGB1555,
151162306a36Sopenharmony_ci	DRM_FORMAT_RGB565,
151262306a36Sopenharmony_ci	DRM_FORMAT_RGB888,
151362306a36Sopenharmony_ci	DRM_FORMAT_XRGB8888,
151462306a36Sopenharmony_ci	DRM_FORMAT_XRGB2101010,
151562306a36Sopenharmony_ci};
151662306a36Sopenharmony_ci
151762306a36Sopenharmony_cistatic const struct jz_soc_info jz4740_soc_info = {
151862306a36Sopenharmony_ci	.needs_dev_clk = true,
151962306a36Sopenharmony_ci	.has_osd = false,
152062306a36Sopenharmony_ci	.map_noncoherent = false,
152162306a36Sopenharmony_ci	.max_width = 800,
152262306a36Sopenharmony_ci	.max_height = 600,
152362306a36Sopenharmony_ci	.max_burst = JZ_LCD_CTRL_BURST_16,
152462306a36Sopenharmony_ci	.formats_f1 = jz4740_formats,
152562306a36Sopenharmony_ci	.num_formats_f1 = ARRAY_SIZE(jz4740_formats),
152662306a36Sopenharmony_ci	/* JZ4740 has only one plane */
152762306a36Sopenharmony_ci};
152862306a36Sopenharmony_ci
152962306a36Sopenharmony_cistatic const struct jz_soc_info jz4725b_soc_info = {
153062306a36Sopenharmony_ci	.needs_dev_clk = false,
153162306a36Sopenharmony_ci	.has_osd = true,
153262306a36Sopenharmony_ci	.map_noncoherent = false,
153362306a36Sopenharmony_ci	.max_width = 800,
153462306a36Sopenharmony_ci	.max_height = 600,
153562306a36Sopenharmony_ci	.max_burst = JZ_LCD_CTRL_BURST_16,
153662306a36Sopenharmony_ci	.formats_f1 = jz4725b_formats_f1,
153762306a36Sopenharmony_ci	.num_formats_f1 = ARRAY_SIZE(jz4725b_formats_f1),
153862306a36Sopenharmony_ci	.formats_f0 = jz4725b_formats_f0,
153962306a36Sopenharmony_ci	.num_formats_f0 = ARRAY_SIZE(jz4725b_formats_f0),
154062306a36Sopenharmony_ci};
154162306a36Sopenharmony_ci
154262306a36Sopenharmony_cistatic const struct jz_soc_info jz4760_soc_info = {
154362306a36Sopenharmony_ci	.needs_dev_clk = false,
154462306a36Sopenharmony_ci	.has_osd = true,
154562306a36Sopenharmony_ci	.map_noncoherent = false,
154662306a36Sopenharmony_ci	.max_width = 1280,
154762306a36Sopenharmony_ci	.max_height = 720,
154862306a36Sopenharmony_ci	.max_burst = JZ_LCD_CTRL_BURST_32,
154962306a36Sopenharmony_ci	.formats_f1 = jz4770_formats_f1,
155062306a36Sopenharmony_ci	.num_formats_f1 = ARRAY_SIZE(jz4770_formats_f1),
155162306a36Sopenharmony_ci	.formats_f0 = jz4770_formats_f0,
155262306a36Sopenharmony_ci	.num_formats_f0 = ARRAY_SIZE(jz4770_formats_f0),
155362306a36Sopenharmony_ci};
155462306a36Sopenharmony_ci
155562306a36Sopenharmony_cistatic const struct jz_soc_info jz4760b_soc_info = {
155662306a36Sopenharmony_ci	.needs_dev_clk = false,
155762306a36Sopenharmony_ci	.has_osd = true,
155862306a36Sopenharmony_ci	.map_noncoherent = false,
155962306a36Sopenharmony_ci	.max_width = 1280,
156062306a36Sopenharmony_ci	.max_height = 720,
156162306a36Sopenharmony_ci	.max_burst = JZ_LCD_CTRL_BURST_64,
156262306a36Sopenharmony_ci	.formats_f1 = jz4770_formats_f1,
156362306a36Sopenharmony_ci	.num_formats_f1 = ARRAY_SIZE(jz4770_formats_f1),
156462306a36Sopenharmony_ci	.formats_f0 = jz4770_formats_f0,
156562306a36Sopenharmony_ci	.num_formats_f0 = ARRAY_SIZE(jz4770_formats_f0),
156662306a36Sopenharmony_ci};
156762306a36Sopenharmony_ci
156862306a36Sopenharmony_cistatic const struct jz_soc_info jz4770_soc_info = {
156962306a36Sopenharmony_ci	.needs_dev_clk = false,
157062306a36Sopenharmony_ci	.has_osd = true,
157162306a36Sopenharmony_ci	.map_noncoherent = true,
157262306a36Sopenharmony_ci	.max_width = 1280,
157362306a36Sopenharmony_ci	.max_height = 720,
157462306a36Sopenharmony_ci	.max_burst = JZ_LCD_CTRL_BURST_64,
157562306a36Sopenharmony_ci	.formats_f1 = jz4770_formats_f1,
157662306a36Sopenharmony_ci	.num_formats_f1 = ARRAY_SIZE(jz4770_formats_f1),
157762306a36Sopenharmony_ci	.formats_f0 = jz4770_formats_f0,
157862306a36Sopenharmony_ci	.num_formats_f0 = ARRAY_SIZE(jz4770_formats_f0),
157962306a36Sopenharmony_ci};
158062306a36Sopenharmony_ci
158162306a36Sopenharmony_cistatic const struct jz_soc_info jz4780_soc_info = {
158262306a36Sopenharmony_ci	.needs_dev_clk = true,
158362306a36Sopenharmony_ci	.has_osd = true,
158462306a36Sopenharmony_ci	.has_alpha = true,
158562306a36Sopenharmony_ci	.use_extended_hwdesc = true,
158662306a36Sopenharmony_ci	.plane_f0_not_working = true,	/* REVISIT */
158762306a36Sopenharmony_ci	.max_width = 4096,
158862306a36Sopenharmony_ci	.max_height = 2048,
158962306a36Sopenharmony_ci	.max_burst = JZ_LCD_CTRL_BURST_64,
159062306a36Sopenharmony_ci	.formats_f1 = jz4770_formats_f1,
159162306a36Sopenharmony_ci	.num_formats_f1 = ARRAY_SIZE(jz4770_formats_f1),
159262306a36Sopenharmony_ci	.formats_f0 = jz4770_formats_f0,
159362306a36Sopenharmony_ci	.num_formats_f0 = ARRAY_SIZE(jz4770_formats_f0),
159462306a36Sopenharmony_ci};
159562306a36Sopenharmony_ci
159662306a36Sopenharmony_cistatic const struct of_device_id ingenic_drm_of_match[] = {
159762306a36Sopenharmony_ci	{ .compatible = "ingenic,jz4740-lcd", .data = &jz4740_soc_info },
159862306a36Sopenharmony_ci	{ .compatible = "ingenic,jz4725b-lcd", .data = &jz4725b_soc_info },
159962306a36Sopenharmony_ci	{ .compatible = "ingenic,jz4760-lcd", .data = &jz4760_soc_info },
160062306a36Sopenharmony_ci	{ .compatible = "ingenic,jz4760b-lcd", .data = &jz4760b_soc_info },
160162306a36Sopenharmony_ci	{ .compatible = "ingenic,jz4770-lcd", .data = &jz4770_soc_info },
160262306a36Sopenharmony_ci	{ .compatible = "ingenic,jz4780-lcd", .data = &jz4780_soc_info },
160362306a36Sopenharmony_ci	{ /* sentinel */ },
160462306a36Sopenharmony_ci};
160562306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, ingenic_drm_of_match);
160662306a36Sopenharmony_ci
160762306a36Sopenharmony_cistatic struct platform_driver ingenic_drm_driver = {
160862306a36Sopenharmony_ci	.driver = {
160962306a36Sopenharmony_ci		.name = "ingenic-drm",
161062306a36Sopenharmony_ci		.pm = pm_sleep_ptr(&ingenic_drm_pm_ops),
161162306a36Sopenharmony_ci		.of_match_table = of_match_ptr(ingenic_drm_of_match),
161262306a36Sopenharmony_ci	},
161362306a36Sopenharmony_ci	.probe = ingenic_drm_probe,
161462306a36Sopenharmony_ci	.remove = ingenic_drm_remove,
161562306a36Sopenharmony_ci};
161662306a36Sopenharmony_ci
161762306a36Sopenharmony_cistatic int ingenic_drm_init(void)
161862306a36Sopenharmony_ci{
161962306a36Sopenharmony_ci	int err;
162062306a36Sopenharmony_ci
162162306a36Sopenharmony_ci	if (drm_firmware_drivers_only())
162262306a36Sopenharmony_ci		return -ENODEV;
162362306a36Sopenharmony_ci
162462306a36Sopenharmony_ci	if (IS_ENABLED(CONFIG_DRM_INGENIC_IPU)) {
162562306a36Sopenharmony_ci		err = platform_driver_register(ingenic_ipu_driver_ptr);
162662306a36Sopenharmony_ci		if (err)
162762306a36Sopenharmony_ci			return err;
162862306a36Sopenharmony_ci	}
162962306a36Sopenharmony_ci
163062306a36Sopenharmony_ci	err = platform_driver_register(&ingenic_drm_driver);
163162306a36Sopenharmony_ci	if (IS_ENABLED(CONFIG_DRM_INGENIC_IPU) && err)
163262306a36Sopenharmony_ci		platform_driver_unregister(ingenic_ipu_driver_ptr);
163362306a36Sopenharmony_ci
163462306a36Sopenharmony_ci	return err;
163562306a36Sopenharmony_ci}
163662306a36Sopenharmony_cimodule_init(ingenic_drm_init);
163762306a36Sopenharmony_ci
163862306a36Sopenharmony_cistatic void ingenic_drm_exit(void)
163962306a36Sopenharmony_ci{
164062306a36Sopenharmony_ci	platform_driver_unregister(&ingenic_drm_driver);
164162306a36Sopenharmony_ci
164262306a36Sopenharmony_ci	if (IS_ENABLED(CONFIG_DRM_INGENIC_IPU))
164362306a36Sopenharmony_ci		platform_driver_unregister(ingenic_ipu_driver_ptr);
164462306a36Sopenharmony_ci}
164562306a36Sopenharmony_cimodule_exit(ingenic_drm_exit);
164662306a36Sopenharmony_ci
164762306a36Sopenharmony_ciMODULE_AUTHOR("Paul Cercueil <paul@crapouillou.net>");
164862306a36Sopenharmony_ciMODULE_DESCRIPTION("DRM driver for the Ingenic SoCs\n");
164962306a36Sopenharmony_ciMODULE_LICENSE("GPL");
1650