1// SPDX-License-Identifier: GPL-2.0
2/*
3 * Copyright (c) 2018 Loongson Technology Co., Ltd.
4 * Copyright (C) 2019 Lemote Inc.
5 * Authors:
6 *	Chen Zhu <zhuchen@loongson.cn>
7 *	Yaling Fang <fangyaling@loongson.cn>
8 *	Dandan Zhang <zhangdandan@loongson.cn>
9 *	Huacai Chen <chenhc@lemote.com>
10 *	Jiaxun Yang <jiaxun.yang@flygoat.com>
11 */
12
13#include <drm/drm_crtc_helper.h>
14#include "loongson_drv.h"
15
16/**
17 * loongson_encoder_destroy
18 *
19 * @encoder: encoder object
20 *
21 * Clean up encoder resources
22 */
23static void loongson_encoder_destroy(struct drm_encoder *encoder)
24{
25	struct loongson_encoder *loongson_encoder = to_loongson_encoder(encoder);
26	drm_encoder_cleanup(encoder);
27	kfree(loongson_encoder);
28}
29
30static int loongson_encoder_atomic_check(struct drm_encoder *encoder,
31				    struct drm_crtc_state *crtc_state,
32				    struct drm_connector_state *conn_state)
33{
34	return 0;
35}
36
37static void loongson_encoder_atomic_mode_set(struct drm_encoder *encoder,
38				struct drm_crtc_state *crtc_state,
39				struct drm_connector_state *conn_state)
40{
41	unsigned long flags;
42	struct loongson_encoder *lenc = to_loongson_encoder(encoder);
43	struct loongson_crtc *lcrtc_origin = lenc->lcrtc;
44	struct loongson_crtc *lcrtc_current = to_loongson_crtc(crtc_state->crtc);
45
46	if (lcrtc_origin->crtc_id != lcrtc_current->crtc_id)
47		lcrtc_origin->cfg_reg |= CFG_PANELSWITCH;
48	else
49		lcrtc_origin->cfg_reg &= ~CFG_PANELSWITCH;
50
51	spin_lock_irqsave(&loongson_reglock, flags);
52	crtc_write(lcrtc_origin, FB_CFG_DVO_REG, lcrtc_origin->cfg_reg);
53	spin_unlock_irqrestore(&loongson_reglock, flags);
54}
55
56/**
57 * These provide the minimum set of functions required to handle a encoder
58 *
59 * Helper operations for encoders
60 */
61static const struct drm_encoder_helper_funcs loongson_encoder_helper_funcs = {
62	.atomic_check = loongson_encoder_atomic_check,
63	.atomic_mode_set = loongson_encoder_atomic_mode_set,
64};
65
66/**
67 * These provide the minimum set of functions required to handle a encoder
68 *
69 * Encoder controls,encoder sit between CRTCs and connectors
70 */
71static const struct drm_encoder_funcs loongson_encoder_encoder_funcs = {
72	.destroy = loongson_encoder_destroy,
73};
74
75static void loongson_hdmi_init(struct loongson_drm_device *ldev, int index)
76{
77	u32 val;
78	int offset = index * 0x10;
79	volatile void __iomem *base = ldev->mmio;
80
81	spin_lock(&loongson_reglock);
82	writel(0x287, base + HDMI_CTRL_REG + offset);
83
84	writel(0x00400040, base + HDMI_ZONEIDLE_REG + offset);
85
86	writel(6272, base + HDMI_AUDIO_NCFG_REG + offset);
87	writel(0x80000000, base + HDMI_AUDIO_CTSCFG_REG + offset);
88
89	writel(0x11, base + HDMI_AUDIO_INFOFRAME_REG + offset);
90	val = readl(base + HDMI_AUDIO_INFOFRAME_REG + offset) | 0x4;
91	writel(val, base + HDMI_AUDIO_INFOFRAME_REG + offset);
92
93	writel(0x1, base + HDMI_AUDIO_SAMPLE_REG + offset);
94	spin_unlock(&loongson_reglock);
95
96	DRM_DEBUG("Loongson HDMI init finish.\n");
97}
98
99/**
100 * loongson_encoder_init
101 *
102 * @dev: point to the drm_device structure
103 *
104 * Init encoder
105 */
106struct drm_encoder *loongson_encoder_init(struct drm_device *dev, unsigned int index)
107{
108	struct drm_encoder *encoder;
109	struct loongson_encoder *loongson_encoder;
110	struct loongson_drm_device *ldev = dev->dev_private;
111
112	loongson_encoder = kzalloc(sizeof(struct loongson_encoder), GFP_KERNEL);
113	if (!loongson_encoder)
114		return NULL;
115
116	loongson_encoder->encoder_id = index;
117	loongson_encoder->i2c = &ldev->i2c_bus[index];
118	loongson_encoder->lcrtc = &ldev->lcrtc[index];
119	loongson_encoder->type = get_encoder_type(ldev, index);
120	encoder = &loongson_encoder->base;
121
122	if (loongson_encoder->type == DRM_MODE_ENCODER_TMDS)
123		loongson_hdmi_init(ldev, index);
124
125	encoder->possible_crtcs = BIT(index);
126	encoder->possible_clones = BIT(1) | BIT(0);
127	/* encoder->possible_crtcs = BIT(1) | BIT(0); */
128
129	drm_encoder_helper_add(encoder, &loongson_encoder_helper_funcs);
130	drm_encoder_init(dev, encoder, &loongson_encoder_encoder_funcs,
131			 loongson_encoder->type, NULL);
132
133	return encoder;
134}
135