Linux Audio

Check our new training course

Linux debugging, profiling, tracing and performance analysis training

Apr 14-17, 2025
Register
Loading...
Note: File does not exist in v3.1.
  1/*
  2 * Copyright (C) STMicroelectronics SA 2014
  3 * Authors: Benjamin Gaignard <benjamin.gaignard@st.com>
  4 *          Fabien Dessenne <fabien.dessenne@st.com>
  5 *          for STMicroelectronics.
  6 * License terms:  GNU General Public License (GPL), version 2
  7 */
  8
  9#include "sti_compositor.h"
 10#include "sti_mixer.h"
 11#include "sti_vtg.h"
 12
 13/* Module parameter to set the background color of the mixer */
 14static unsigned int bkg_color = 0x000000;
 15MODULE_PARM_DESC(bkgcolor, "Value of the background color 0xRRGGBB");
 16module_param_named(bkgcolor, bkg_color, int, 0644);
 17
 18/* Identity: G=Y , B=Cb , R=Cr */
 19static const u32 mixerColorSpaceMatIdentity[] = {
 20	0x10000000, 0x00000000, 0x10000000, 0x00001000,
 21	0x00000000, 0x00000000, 0x00000000, 0x00000000
 22};
 23
 24/* regs offset */
 25#define GAM_MIXER_CTL      0x00
 26#define GAM_MIXER_BKC      0x04
 27#define GAM_MIXER_BCO      0x0C
 28#define GAM_MIXER_BCS      0x10
 29#define GAM_MIXER_AVO      0x28
 30#define GAM_MIXER_AVS      0x2C
 31#define GAM_MIXER_CRB      0x34
 32#define GAM_MIXER_ACT      0x38
 33#define GAM_MIXER_MBP      0x3C
 34#define GAM_MIXER_MX0      0x80
 35
 36/* id for depth of CRB reg */
 37#define GAM_DEPTH_VID0_ID  1
 38#define GAM_DEPTH_VID1_ID  2
 39#define GAM_DEPTH_GDP0_ID  3
 40#define GAM_DEPTH_GDP1_ID  4
 41#define GAM_DEPTH_GDP2_ID  5
 42#define GAM_DEPTH_GDP3_ID  6
 43#define GAM_DEPTH_MASK_ID  7
 44
 45/* mask in CTL reg */
 46#define GAM_CTL_BACK_MASK  BIT(0)
 47#define GAM_CTL_VID0_MASK  BIT(1)
 48#define GAM_CTL_VID1_MASK  BIT(2)
 49#define GAM_CTL_GDP0_MASK  BIT(3)
 50#define GAM_CTL_GDP1_MASK  BIT(4)
 51#define GAM_CTL_GDP2_MASK  BIT(5)
 52#define GAM_CTL_GDP3_MASK  BIT(6)
 53#define GAM_CTL_CURSOR_MASK BIT(9)
 54
 55const char *sti_mixer_to_str(struct sti_mixer *mixer)
 56{
 57	switch (mixer->id) {
 58	case STI_MIXER_MAIN:
 59		return "MAIN_MIXER";
 60	case STI_MIXER_AUX:
 61		return "AUX_MIXER";
 62	default:
 63		return "<UNKNOWN MIXER>";
 64	}
 65}
 66
 67static inline u32 sti_mixer_reg_read(struct sti_mixer *mixer, u32 reg_id)
 68{
 69	return readl(mixer->regs + reg_id);
 70}
 71
 72static inline void sti_mixer_reg_write(struct sti_mixer *mixer,
 73				       u32 reg_id, u32 val)
 74{
 75	writel(val, mixer->regs + reg_id);
 76}
 77
 78#define DBGFS_DUMP(reg) seq_printf(s, "\n  %-25s 0x%08X", #reg, \
 79				   sti_mixer_reg_read(mixer, reg))
 80
 81static void mixer_dbg_ctl(struct seq_file *s, int val)
 82{
 83	unsigned int i;
 84	int count = 0;
 85	char *const disp_layer[] = {"BKG", "VID0", "VID1", "GDP0",
 86				    "GDP1", "GDP2", "GDP3"};
 87
 88	seq_puts(s, "\tEnabled: ");
 89	for (i = 0; i < 7; i++) {
 90		if (val & 1) {
 91			seq_printf(s, "%s ", disp_layer[i]);
 92			count++;
 93		}
 94		val = val >> 1;
 95	}
 96
 97	val = val >> 2;
 98	if (val & 1) {
 99		seq_puts(s, "CURS ");
100		count++;
101	}
102	if (!count)
103		seq_puts(s, "Nothing");
104}
105
106static void mixer_dbg_crb(struct seq_file *s, int val)
107{
108	int i;
109
110	seq_puts(s, "\tDepth: ");
111	for (i = 0; i < GAM_MIXER_NB_DEPTH_LEVEL; i++) {
112		switch (val & GAM_DEPTH_MASK_ID) {
113		case GAM_DEPTH_VID0_ID:
114			seq_puts(s, "VID0");
115			break;
116		case GAM_DEPTH_VID1_ID:
117			seq_puts(s, "VID1");
118			break;
119		case GAM_DEPTH_GDP0_ID:
120			seq_puts(s, "GDP0");
121			break;
122		case GAM_DEPTH_GDP1_ID:
123			seq_puts(s, "GDP1");
124			break;
125		case GAM_DEPTH_GDP2_ID:
126			seq_puts(s, "GDP2");
127			break;
128		case GAM_DEPTH_GDP3_ID:
129			seq_puts(s, "GDP3");
130			break;
131		default:
132			seq_puts(s, "---");
133		}
134
135		if (i < GAM_MIXER_NB_DEPTH_LEVEL - 1)
136			seq_puts(s, " < ");
137		val = val >> 3;
138	}
139}
140
141static void mixer_dbg_mxn(struct seq_file *s, void *addr)
142{
143	int i;
144
145	for (i = 1; i < 8; i++)
146		seq_printf(s, "-0x%08X", (int)readl(addr + i * 4));
147}
148
149static int mixer_dbg_show(struct seq_file *s, void *arg)
150{
151	struct drm_info_node *node = s->private;
152	struct sti_mixer *mixer = (struct sti_mixer *)node->info_ent->data;
153	struct drm_device *dev = node->minor->dev;
154	int ret;
155
156	ret = mutex_lock_interruptible(&dev->struct_mutex);
157	if (ret)
158		return ret;
159
160	seq_printf(s, "%s: (vaddr = 0x%p)",
161		   sti_mixer_to_str(mixer), mixer->regs);
162
163	DBGFS_DUMP(GAM_MIXER_CTL);
164	mixer_dbg_ctl(s, sti_mixer_reg_read(mixer, GAM_MIXER_CTL));
165	DBGFS_DUMP(GAM_MIXER_BKC);
166	DBGFS_DUMP(GAM_MIXER_BCO);
167	DBGFS_DUMP(GAM_MIXER_BCS);
168	DBGFS_DUMP(GAM_MIXER_AVO);
169	DBGFS_DUMP(GAM_MIXER_AVS);
170	DBGFS_DUMP(GAM_MIXER_CRB);
171	mixer_dbg_crb(s, sti_mixer_reg_read(mixer, GAM_MIXER_CRB));
172	DBGFS_DUMP(GAM_MIXER_ACT);
173	DBGFS_DUMP(GAM_MIXER_MBP);
174	DBGFS_DUMP(GAM_MIXER_MX0);
175	mixer_dbg_mxn(s, mixer->regs + GAM_MIXER_MX0);
176	seq_puts(s, "\n");
177
178	mutex_unlock(&dev->struct_mutex);
179	return 0;
180}
181
182static struct drm_info_list mixer0_debugfs_files[] = {
183	{ "mixer_main", mixer_dbg_show, 0, NULL },
184};
185
186static struct drm_info_list mixer1_debugfs_files[] = {
187	{ "mixer_aux", mixer_dbg_show, 0, NULL },
188};
189
190static int mixer_debugfs_init(struct sti_mixer *mixer, struct drm_minor *minor)
191{
192	unsigned int i;
193	struct drm_info_list *mixer_debugfs_files;
194	int nb_files;
195
196	switch (mixer->id) {
197	case STI_MIXER_MAIN:
198		mixer_debugfs_files = mixer0_debugfs_files;
199		nb_files = ARRAY_SIZE(mixer0_debugfs_files);
200		break;
201	case STI_MIXER_AUX:
202		mixer_debugfs_files = mixer1_debugfs_files;
203		nb_files = ARRAY_SIZE(mixer1_debugfs_files);
204		break;
205	default:
206		return -EINVAL;
207	}
208
209	for (i = 0; i < nb_files; i++)
210		mixer_debugfs_files[i].data = mixer;
211
212	return drm_debugfs_create_files(mixer_debugfs_files,
213					nb_files,
214					minor->debugfs_root, minor);
215}
216
217void sti_mixer_set_background_status(struct sti_mixer *mixer, bool enable)
218{
219	u32 val = sti_mixer_reg_read(mixer, GAM_MIXER_CTL);
220
221	val &= ~GAM_CTL_BACK_MASK;
222	val |= enable;
223	sti_mixer_reg_write(mixer, GAM_MIXER_CTL, val);
224}
225
226static void sti_mixer_set_background_color(struct sti_mixer *mixer,
227					   unsigned int rgb)
228{
229	sti_mixer_reg_write(mixer, GAM_MIXER_BKC, rgb);
230}
231
232static void sti_mixer_set_background_area(struct sti_mixer *mixer,
233					  struct drm_display_mode *mode)
234{
235	u32 ydo, xdo, yds, xds;
236
237	ydo = sti_vtg_get_line_number(*mode, 0);
238	yds = sti_vtg_get_line_number(*mode, mode->vdisplay - 1);
239	xdo = sti_vtg_get_pixel_number(*mode, 0);
240	xds = sti_vtg_get_pixel_number(*mode, mode->hdisplay - 1);
241
242	sti_mixer_reg_write(mixer, GAM_MIXER_BCO, ydo << 16 | xdo);
243	sti_mixer_reg_write(mixer, GAM_MIXER_BCS, yds << 16 | xds);
244}
245
246int sti_mixer_set_plane_depth(struct sti_mixer *mixer, struct sti_plane *plane)
247{
248	int plane_id, depth = plane->zorder;
249	unsigned int i;
250	u32 mask, val;
251
252	if ((depth < 1) || (depth > GAM_MIXER_NB_DEPTH_LEVEL))
253		return 1;
254
255	switch (plane->desc) {
256	case STI_GDP_0:
257		plane_id = GAM_DEPTH_GDP0_ID;
258		break;
259	case STI_GDP_1:
260		plane_id = GAM_DEPTH_GDP1_ID;
261		break;
262	case STI_GDP_2:
263		plane_id = GAM_DEPTH_GDP2_ID;
264		break;
265	case STI_GDP_3:
266		plane_id = GAM_DEPTH_GDP3_ID;
267		break;
268	case STI_HQVDP_0:
269		plane_id = GAM_DEPTH_VID0_ID;
270		break;
271	case STI_CURSOR:
272		/* no need to set depth for cursor */
273		return 0;
274	default:
275		DRM_ERROR("Unknown plane %d\n", plane->desc);
276		return 1;
277	}
278
279	/* Search if a previous depth was already assigned to the plane */
280	val = sti_mixer_reg_read(mixer, GAM_MIXER_CRB);
281	for (i = 0; i < GAM_MIXER_NB_DEPTH_LEVEL; i++) {
282		mask = GAM_DEPTH_MASK_ID << (3 * i);
283		if ((val & mask) == plane_id << (3 * i))
284			break;
285	}
286
287	mask |= GAM_DEPTH_MASK_ID << (3 * (depth - 1));
288	plane_id = plane_id << (3 * (depth - 1));
289
290	DRM_DEBUG_DRIVER("%s %s depth=%d\n", sti_mixer_to_str(mixer),
291			 sti_plane_to_str(plane), depth);
292	dev_dbg(mixer->dev, "GAM_MIXER_CRB val 0x%x mask 0x%x\n",
293		plane_id, mask);
294
295	val &= ~mask;
296	val |= plane_id;
297	sti_mixer_reg_write(mixer, GAM_MIXER_CRB, val);
298
299	dev_dbg(mixer->dev, "Read GAM_MIXER_CRB 0x%x\n",
300		sti_mixer_reg_read(mixer, GAM_MIXER_CRB));
301	return 0;
302}
303
304int sti_mixer_active_video_area(struct sti_mixer *mixer,
305				struct drm_display_mode *mode)
306{
307	u32 ydo, xdo, yds, xds;
308
309	ydo = sti_vtg_get_line_number(*mode, 0);
310	yds = sti_vtg_get_line_number(*mode, mode->vdisplay - 1);
311	xdo = sti_vtg_get_pixel_number(*mode, 0);
312	xds = sti_vtg_get_pixel_number(*mode, mode->hdisplay - 1);
313
314	DRM_DEBUG_DRIVER("%s active video area xdo:%d ydo:%d xds:%d yds:%d\n",
315			 sti_mixer_to_str(mixer), xdo, ydo, xds, yds);
316	sti_mixer_reg_write(mixer, GAM_MIXER_AVO, ydo << 16 | xdo);
317	sti_mixer_reg_write(mixer, GAM_MIXER_AVS, yds << 16 | xds);
318
319	sti_mixer_set_background_color(mixer, bkg_color);
320
321	sti_mixer_set_background_area(mixer, mode);
322	sti_mixer_set_background_status(mixer, true);
323	return 0;
324}
325
326static u32 sti_mixer_get_plane_mask(struct sti_plane *plane)
327{
328	switch (plane->desc) {
329	case STI_BACK:
330		return GAM_CTL_BACK_MASK;
331	case STI_GDP_0:
332		return GAM_CTL_GDP0_MASK;
333	case STI_GDP_1:
334		return GAM_CTL_GDP1_MASK;
335	case STI_GDP_2:
336		return GAM_CTL_GDP2_MASK;
337	case STI_GDP_3:
338		return GAM_CTL_GDP3_MASK;
339	case STI_HQVDP_0:
340		return GAM_CTL_VID0_MASK;
341	case STI_CURSOR:
342		return GAM_CTL_CURSOR_MASK;
343	default:
344		return 0;
345	}
346}
347
348int sti_mixer_set_plane_status(struct sti_mixer *mixer,
349			       struct sti_plane *plane, bool status)
350{
351	u32 mask, val;
352
353	DRM_DEBUG_DRIVER("%s %s %s\n", status ? "enable" : "disable",
354			 sti_mixer_to_str(mixer), sti_plane_to_str(plane));
355
356	mask = sti_mixer_get_plane_mask(plane);
357	if (!mask) {
358		DRM_ERROR("Can't find layer mask\n");
359		return -EINVAL;
360	}
361
362	val = sti_mixer_reg_read(mixer, GAM_MIXER_CTL);
363	val &= ~mask;
364	val |= status ? mask : 0;
365	sti_mixer_reg_write(mixer, GAM_MIXER_CTL, val);
366
367	return 0;
368}
369
370void sti_mixer_set_matrix(struct sti_mixer *mixer)
371{
372	unsigned int i;
373
374	for (i = 0; i < ARRAY_SIZE(mixerColorSpaceMatIdentity); i++)
375		sti_mixer_reg_write(mixer, GAM_MIXER_MX0 + (i * 4),
376				    mixerColorSpaceMatIdentity[i]);
377}
378
379struct sti_mixer *sti_mixer_create(struct device *dev,
380				   struct drm_device *drm_dev,
381				   int id,
382				   void __iomem *baseaddr)
383{
384	struct sti_mixer *mixer = devm_kzalloc(dev, sizeof(*mixer), GFP_KERNEL);
385	struct device_node *np = dev->of_node;
386
387	dev_dbg(dev, "%s\n", __func__);
388	if (!mixer) {
389		DRM_ERROR("Failed to allocated memory for mixer\n");
390		return NULL;
391	}
392	mixer->regs = baseaddr;
393	mixer->dev = dev;
394	mixer->id = id;
395
396	if (of_device_is_compatible(np, "st,stih416-compositor"))
397		sti_mixer_set_matrix(mixer);
398
399	DRM_DEBUG_DRIVER("%s created. Regs=%p\n",
400			 sti_mixer_to_str(mixer), mixer->regs);
401
402	if (mixer_debugfs_init(mixer, drm_dev->primary))
403		DRM_ERROR("MIXER debugfs setup failed\n");
404
405	return mixer;
406}