android13/kernel-5.10/drivers/gpu/drm/rockchip/rk618/rk618_vif.c

262 lines
7.0 KiB
C

// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2017 Rockchip Electronics Co. Ltd.
*
* Author: Wyon Bi <bivvy.bi@rock-chips.com>
*/
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/mfd/rk618.h>
#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/regmap.h>
#include <drm/drm_of.h>
#include <drm/drm_drv.h>
#include <video/videomode.h>
#define RK618_VIF0_REG0 0x0000
#define VIF_ENABLE HIWORD_UPDATE(1, 0, 0)
#define VIF_DISABLE HIWORD_UPDATE(0, 0, 0)
#define RK618_VIF0_REG1 0x0004
#define VIF_FRAME_VST(x) UPDATE(x, 27, 16)
#define VIF_FRAME_HST(x) UPDATE(x, 11, 0)
#define RK618_VIF0_REG2 0x0008
#define VIF_HS_END(x) UPDATE(x, 23, 16)
#define VIF_HTOTAL(x) UPDATE(x, 11, 0)
#define RK618_VIF0_REG3 0x000c
#define VIF_HACT_END(x) UPDATE(x, 27, 16)
#define VIF_HACT_ST(x) UPDATE(x, 11, 0)
#define RK618_VIF0_REG4 0x0010
#define VIF_VS_END(x) UPDATE(x, 23, 16)
#define VIF_VTOTAL(x) UPDATE(x, 11, 0)
#define RK618_VIF0_REG5 0x0014
#define VIF_VACT_END(x) UPDATE(x, 27, 16)
#define VIF_VACT_ST(x) UPDATE(x, 11, 0)
#define RK618_VIF1_REG0 0x0018
#define RK618_VIF1_REG1 0x001c
#define RK618_VIF1_REG2 0x0020
#define RK618_VIF1_REG3 0x0024
#define RK618_VIF1_REG4 0x0028
#define RK618_VIF1_REG5 0x002c
struct rk618_vif {
struct drm_bridge base;
struct drm_bridge *bridge;
struct drm_display_mode mode;
struct device *dev;
struct regmap *regmap;
struct clk *vif_clk;
struct clk *vif_pre_clk;
};
static inline struct rk618_vif *bridge_to_vif(struct drm_bridge *bridge)
{
return container_of(bridge, struct rk618_vif, base);
}
static void rk618_vif_enable(struct rk618_vif *vif)
{
regmap_write(vif->regmap, RK618_VIF0_REG0, VIF_ENABLE);
}
static void rk618_vif_disable(struct rk618_vif *vif)
{
regmap_write(vif->regmap, RK618_VIF0_REG0, VIF_DISABLE);
}
static void rk618_vif_init(struct rk618_vif *vif,
const struct drm_display_mode *mode)
{
struct videomode vm;
u32 vif_frame_vst, vif_frame_hst;
u32 vif_hs_end, vif_htotal, vif_hact_end, vif_hact_st;
u32 vif_vs_end, vif_vtotal, vif_vact_end, vif_vact_st;
drm_display_mode_to_videomode(mode, &vm);
if (!strcmp(mode->name, "1920x1080")) {
vif_frame_vst = 0x001;
vif_frame_hst = 0x0cb;
} else if (!strcmp(mode->name, "1600x900")) {
vif_frame_vst = 0x001;
vif_frame_hst = 0x327;
} else if (!strcmp(mode->name, "1280x720")) {
vif_frame_vst = 0x001;
vif_frame_hst = 0x0cf;
} else {
vif_frame_vst = 0x001;
vif_frame_hst = 0x001;
}
dev_dbg(vif->dev, "vif_frame_vst=%d, vif_frame_hst=%d\n",
vif_frame_vst, vif_frame_hst);
vif_hs_end = vm.hsync_len;
vif_htotal = vm.hsync_len + vm.hback_porch + vm.hfront_porch +
vm.hactive;
vif_hact_end = vm.hsync_len + vm.hback_porch + vm.hactive;
vif_hact_st = vm.hsync_len + vm.hback_porch;
vif_vs_end = vm.vsync_len;
vif_vtotal = vm.vsync_len + vm.vback_porch + vm.vfront_porch +
vm.vactive;
vif_vact_end = vm.vsync_len + vm.vback_porch + vm.vactive;
vif_vact_st = vm.vsync_len + vm.vback_porch;
regmap_write(vif->regmap, RK618_VIF0_REG1,
VIF_FRAME_VST(vif_frame_vst) |
VIF_FRAME_HST(vif_frame_hst));
regmap_write(vif->regmap, RK618_VIF0_REG2,
VIF_HS_END(vif_hs_end) | VIF_HTOTAL(vif_htotal));
regmap_write(vif->regmap, RK618_VIF0_REG3,
VIF_HACT_END(vif_hact_end) | VIF_HACT_ST(vif_hact_st));
regmap_write(vif->regmap, RK618_VIF0_REG4,
VIF_VS_END(vif_vs_end) | VIF_VTOTAL(vif_vtotal));
regmap_write(vif->regmap, RK618_VIF0_REG5,
VIF_VACT_END(vif_vact_end) | VIF_VACT_ST(vif_vact_st));
}
static void rk618_vif_bridge_enable(struct drm_bridge *bridge)
{
struct rk618_vif *vif = bridge_to_vif(bridge);
const struct drm_display_mode *mode = &vif->mode;
long rate;
clk_set_parent(vif->vif_clk, vif->vif_pre_clk);
rate = clk_round_rate(vif->vif_clk, mode->clock * 1000);
clk_set_rate(vif->vif_clk, rate);
clk_prepare_enable(vif->vif_clk);
rk618_vif_disable(vif);
rk618_vif_init(vif, mode);
rk618_vif_enable(vif);
}
static void rk618_vif_bridge_disable(struct drm_bridge *bridge)
{
struct rk618_vif *vif = bridge_to_vif(bridge);
rk618_vif_disable(vif);
clk_disable_unprepare(vif->vif_clk);
}
static void rk618_vif_bridge_mode_set(struct drm_bridge *bridge,
const struct drm_display_mode *mode,
const struct drm_display_mode *adjusted)
{
struct rk618_vif *vif = bridge_to_vif(bridge);
drm_mode_copy(&vif->mode, adjusted);
}
static int rk618_vif_bridge_attach(struct drm_bridge *bridge,
enum drm_bridge_attach_flags flags)
{
struct rk618_vif *vif = bridge_to_vif(bridge);
struct device *dev = vif->dev;
struct device_node *endpoint;
int ret;
endpoint = of_graph_get_endpoint_by_regs(dev->of_node, 1, -1);
if (endpoint && of_device_is_available(endpoint)) {
struct device_node *remote;
remote = of_graph_get_remote_port_parent(endpoint);
of_node_put(endpoint);
if (!remote || !of_device_is_available(remote))
return -ENODEV;
vif->bridge = of_drm_find_bridge(remote);
of_node_put(remote);
if (!vif->bridge)
return -EPROBE_DEFER;
ret = drm_bridge_attach(bridge->encoder, vif->bridge, bridge, 0);
if (ret) {
dev_err(dev, "failed to attach bridge\n");
return ret;
}
}
return 0;
}
static const struct drm_bridge_funcs rk618_vif_bridge_funcs = {
.enable = rk618_vif_bridge_enable,
.disable = rk618_vif_bridge_disable,
.mode_set = rk618_vif_bridge_mode_set,
.attach = rk618_vif_bridge_attach,
};
static int rk618_vif_probe(struct platform_device *pdev)
{
struct rk618 *rk618 = dev_get_drvdata(pdev->dev.parent);
struct device *dev = &pdev->dev;
struct rk618_vif *vif;
int ret;
if (!of_device_is_available(dev->of_node))
return -ENODEV;
vif = devm_kzalloc(dev, sizeof(*vif), GFP_KERNEL);
if (!vif)
return -ENOMEM;
vif->dev = dev;
vif->regmap = rk618->regmap;
platform_set_drvdata(pdev, vif);
vif->vif_clk = devm_clk_get(dev, "vif");
if (IS_ERR(vif->vif_clk)) {
ret = PTR_ERR(vif->vif_clk);
dev_err(dev, "failed to get vif clock: %d\n", ret);
return ret;
}
vif->vif_pre_clk = devm_clk_get(dev, "vif_pre");
if (IS_ERR(vif->vif_pre_clk)) {
ret = PTR_ERR(vif->vif_pre_clk);
dev_err(dev, "failed to get vif pre clock: %d\n", ret);
return ret;
}
vif->base.funcs = &rk618_vif_bridge_funcs;
vif->base.of_node = dev->of_node;
drm_bridge_add(&vif->base);
return 0;
}
static int rk618_vif_remove(struct platform_device *pdev)
{
struct rk618_vif *vif = platform_get_drvdata(pdev);
drm_bridge_remove(&vif->base);
return 0;
}
static const struct of_device_id rk618_vif_of_match[] = {
{ .compatible = "rockchip,rk618-vif", },
{},
};
MODULE_DEVICE_TABLE(of, rk618_vif_of_match);
static struct platform_driver rk618_vif_driver = {
.driver = {
.name = "rk618-vif",
.of_match_table = of_match_ptr(rk618_vif_of_match),
},
.probe = rk618_vif_probe,
.remove = rk618_vif_remove,
};
module_platform_driver(rk618_vif_driver);
MODULE_AUTHOR("Wyon Bi <bivvy.bi@rock-chips.com>");
MODULE_DESCRIPTION("Rockchip RK618 VIF driver");
MODULE_LICENSE("GPL v2");