// SPDX-License-Identifier: GPL-2.0
//
// Ingenic JZ47xx KMS driver
//
// Copyright (C) 2019, Paul Cercueil <paul@crapouillou.net>
#include "ingenic-drm.h"
#include <linux/component.h>
#include <linux/clk.h>
#include <linux/dma-mapping.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/of_device.h>
#include <linux/of_reserved_mem.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_bridge.h>
#include <drm/drm_color_mgmt.h>
#include <drm/drm_crtc.h>
#include <drm/drm_crtc_helper.h>
#include <drm/drm_drv.h>
#include <drm/drm_gem_cma_helper.h>
#include <drm/drm_fb_cma_helper.h>
#include <drm/drm_fb_helper.h>
#include <drm/drm_fourcc.h>
#include <drm/drm_gem_framebuffer_helper.h>
#include <drm/drm_irq.h>
#include <drm/drm_managed.h>
#include <drm/drm_of.h>
#include <drm/drm_panel.h>
#include <drm/drm_plane.h>
#include <drm/drm_plane_helper.h>
#include <drm/drm_probe_helper.h>
#include <drm/drm_simple_kms_helper.h>
#include <drm/drm_vblank.h>
struct ingenic_dma_hwdesc {
u32 next;
u32 addr;
u32 id;
u32 cmd;
} __aligned(16);
struct ingenic_dma_hwdescs {
struct ingenic_dma_hwdesc hwdesc_f0;
struct ingenic_dma_hwdesc hwdesc_f1;
struct ingenic_dma_hwdesc hwdesc_pal;
u16 palette[256] __aligned(16);
};
struct jz_soc_info {
bool needs_dev_clk;
bool has_osd;
unsigned int max_width, max_height;
const u32 *formats_f0, *formats_f1;
unsigned int num_formats_f0, num_formats_f1;
};
struct ingenic_drm {
struct drm_device drm;
/*
* f1 (aka. foreground1) is our primary plane, on top of which
* f0 (aka. foreground0) can be overlayed. Z-order is fixed in
* hardware and cannot be changed.
*/
struct drm_plane f0, f1, *ipu_plane;
struct drm_crtc crtc;
struct device *dev;
struct regmap *map;
struct clk *lcd_clk, *pix_clk;
const struct jz_soc_info *soc_info;
struct ingenic_dma_hwdescs *dma_hwdescs;
dma_addr_t dma_hwdescs_phys;
bool panel_is_sharp;
bool no_vblank;
/*
* clk_mutex is used to synchronize the pixel clock rate update with
* the VBLANK. When the pixel clock's parent clock needs to be updated,
* clock_nb's notifier function will lock the mutex, then wait until the
* next VBLANK. At that point, the parent clock's rate can be updated,
* and the mutex is then unlocked. If an atomic commit happens in the
* meantime, it will lock on the mutex, effectively waiting until the
* clock update process finishes. Finally, the pixel clock's rate will
* be recomputed when the mutex has been released, in the pending atomic
* commit, or a future one.
*/
struct mutex clk_mutex;
bool update_clk_rate;
struct notifier_block clock_nb;
};
static bool ingenic_drm_writeable_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
case JZ_REG_LCD_IID:
case JZ_REG_LCD_SA0:
case JZ_REG_LCD_FID0:
case JZ_REG_LCD_CMD0:
case JZ_REG_LCD_SA1:
case JZ_REG_LCD_FID1:
case JZ_REG_LCD_CMD1:
return false;
default:
return true;
}
}
static const struct regmap_config ingenic_drm_regmap_config = {
.reg_bits = 32,
.val_bits = 32,
.reg_stride = 4,
.max_register = JZ_REG_LCD_SIZE1,
.writeable_reg = ingenic_drm_writeable_reg,
};
static