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 */
loongson_encoder_destroy(struct drm_encoder * encoder)23 static 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
loongson_encoder_atomic_check(struct drm_encoder * encoder,struct drm_crtc_state * crtc_state,struct drm_connector_state * conn_state)30 static 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
loongson_encoder_atomic_mode_set(struct drm_encoder * encoder,struct drm_crtc_state * crtc_state,struct drm_connector_state * conn_state)37 static 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 */
61 static 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 */
71 static const struct drm_encoder_funcs loongson_encoder_encoder_funcs = {
72 .destroy = loongson_encoder_destroy,
73 };
74
loongson_hdmi_init(struct loongson_drm_device * ldev,int index)75 static 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 */
loongson_encoder_init(struct drm_device * dev,unsigned int index)106 struct 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