1// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Copyright (C) 2018 BayLibre, SAS
4 * Author: Maxime Jourdan <mjourdan@baylibre.com>
5 */
6
7#include <media/v4l2-mem2mem.h>
8#include <media/videobuf2-dma-contig.h>
9
10#include "codec_mpeg12.h"
11#include "dos_regs.h"
12#include "vdec_helpers.h"
13
14#define SIZE_WORKSPACE		SZ_128K
15/* Offset substracted by the firmware from the workspace paddr */
16#define WORKSPACE_OFFSET	(5 * SZ_1K)
17
18/* map firmware registers to known MPEG1/2 functions */
19#define MREG_SEQ_INFO		AV_SCRATCH_4
20	#define MPEG2_SEQ_DAR_MASK	GENMASK(3, 0)
21	#define MPEG2_DAR_4_3		2
22	#define MPEG2_DAR_16_9		3
23	#define MPEG2_DAR_221_100	4
24#define MREG_PIC_INFO		AV_SCRATCH_5
25#define MREG_PIC_WIDTH		AV_SCRATCH_6
26#define MREG_PIC_HEIGHT		AV_SCRATCH_7
27#define MREG_BUFFERIN		AV_SCRATCH_8
28#define MREG_BUFFEROUT		AV_SCRATCH_9
29#define MREG_CMD		AV_SCRATCH_A
30#define MREG_CO_MV_START	AV_SCRATCH_B
31#define MREG_ERROR_COUNT	AV_SCRATCH_C
32#define MREG_FRAME_OFFSET	AV_SCRATCH_D
33#define MREG_WAIT_BUFFER	AV_SCRATCH_E
34#define MREG_FATAL_ERROR	AV_SCRATCH_F
35
36#define PICINFO_PROG		0x00008000
37#define PICINFO_TOP_FIRST	0x00002000
38
39struct codec_mpeg12 {
40	/* Buffer for the MPEG1/2 Workspace */
41	void	  *workspace_vaddr;
42	dma_addr_t workspace_paddr;
43};
44
45static const u8 eos_sequence[SZ_1K] = { 0x00, 0x00, 0x01, 0xB7 };
46
47static const u8 *codec_mpeg12_eos_sequence(u32 *len)
48{
49	*len = ARRAY_SIZE(eos_sequence);
50	return eos_sequence;
51}
52
53static int codec_mpeg12_can_recycle(struct amvdec_core *core)
54{
55	return !amvdec_read_dos(core, MREG_BUFFERIN);
56}
57
58static void codec_mpeg12_recycle(struct amvdec_core *core, u32 buf_idx)
59{
60	amvdec_write_dos(core, MREG_BUFFERIN, buf_idx + 1);
61}
62
63static int codec_mpeg12_start(struct amvdec_session *sess)
64{
65	struct amvdec_core *core = sess->core;
66	struct codec_mpeg12 *mpeg12;
67	int ret;
68
69	mpeg12 = kzalloc(sizeof(*mpeg12), GFP_KERNEL);
70	if (!mpeg12)
71		return -ENOMEM;
72
73	/* Allocate some memory for the MPEG1/2 decoder's state */
74	mpeg12->workspace_vaddr = dma_alloc_coherent(core->dev, SIZE_WORKSPACE,
75						     &mpeg12->workspace_paddr,
76						     GFP_KERNEL);
77	if (!mpeg12->workspace_vaddr) {
78		dev_err(core->dev, "Failed to request MPEG 1/2 Workspace\n");
79		ret = -ENOMEM;
80		goto free_mpeg12;
81	}
82
83	ret = amvdec_set_canvases(sess, (u32[]){ AV_SCRATCH_0, 0 },
84					(u32[]){ 8, 0 });
85	if (ret)
86		goto free_workspace;
87
88	amvdec_write_dos(core, POWER_CTL_VLD, BIT(4));
89	amvdec_write_dos(core, MREG_CO_MV_START,
90			 mpeg12->workspace_paddr + WORKSPACE_OFFSET);
91
92	amvdec_write_dos(core, MPEG1_2_REG, 0);
93	amvdec_write_dos(core, PSCALE_CTRL, 0);
94	amvdec_write_dos(core, PIC_HEAD_INFO, 0x380);
95	amvdec_write_dos(core, M4_CONTROL_REG, 0);
96	amvdec_write_dos(core, MREG_BUFFERIN, 0);
97	amvdec_write_dos(core, MREG_BUFFEROUT, 0);
98	amvdec_write_dos(core, MREG_CMD, (sess->width << 16) | sess->height);
99	amvdec_write_dos(core, MREG_ERROR_COUNT, 0);
100	amvdec_write_dos(core, MREG_FATAL_ERROR, 0);
101	amvdec_write_dos(core, MREG_WAIT_BUFFER, 0);
102
103	sess->keyframe_found = 1;
104	sess->priv = mpeg12;
105
106	return 0;
107
108free_workspace:
109	dma_free_coherent(core->dev, SIZE_WORKSPACE, mpeg12->workspace_vaddr,
110			  mpeg12->workspace_paddr);
111free_mpeg12:
112	kfree(mpeg12);
113
114	return ret;
115}
116
117static int codec_mpeg12_stop(struct amvdec_session *sess)
118{
119	struct codec_mpeg12 *mpeg12 = sess->priv;
120	struct amvdec_core *core = sess->core;
121
122	if (mpeg12->workspace_vaddr)
123		dma_free_coherent(core->dev, SIZE_WORKSPACE,
124				  mpeg12->workspace_vaddr,
125				  mpeg12->workspace_paddr);
126
127	return 0;
128}
129
130static void codec_mpeg12_update_dar(struct amvdec_session *sess)
131{
132	struct amvdec_core *core = sess->core;
133	u32 seq = amvdec_read_dos(core, MREG_SEQ_INFO);
134	u32 ar = seq & MPEG2_SEQ_DAR_MASK;
135
136	switch (ar) {
137	case MPEG2_DAR_4_3:
138		amvdec_set_par_from_dar(sess, 4, 3);
139		break;
140	case MPEG2_DAR_16_9:
141		amvdec_set_par_from_dar(sess, 16, 9);
142		break;
143	case MPEG2_DAR_221_100:
144		amvdec_set_par_from_dar(sess, 221, 100);
145		break;
146	default:
147		sess->pixelaspect.numerator = 1;
148		sess->pixelaspect.denominator = 1;
149		break;
150	}
151}
152
153static irqreturn_t codec_mpeg12_threaded_isr(struct amvdec_session *sess)
154{
155	struct amvdec_core *core = sess->core;
156	u32 reg;
157	u32 pic_info;
158	u32 is_progressive;
159	u32 buffer_index;
160	u32 field = V4L2_FIELD_NONE;
161	u32 offset;
162
163	amvdec_write_dos(core, ASSIST_MBOX1_CLR_REG, 1);
164	reg = amvdec_read_dos(core, MREG_FATAL_ERROR);
165	if (reg == 1) {
166		dev_err(core->dev, "MPEG1/2 fatal error\n");
167		amvdec_abort(sess);
168		return IRQ_HANDLED;
169	}
170
171	reg = amvdec_read_dos(core, MREG_BUFFEROUT);
172	if (!reg)
173		return IRQ_HANDLED;
174
175	/* Unclear what this means */
176	if ((reg & GENMASK(23, 17)) == GENMASK(23, 17))
177		goto end;
178
179	pic_info = amvdec_read_dos(core, MREG_PIC_INFO);
180	is_progressive = pic_info & PICINFO_PROG;
181
182	if (!is_progressive)
183		field = (pic_info & PICINFO_TOP_FIRST) ?
184			V4L2_FIELD_INTERLACED_TB :
185			V4L2_FIELD_INTERLACED_BT;
186
187	codec_mpeg12_update_dar(sess);
188	buffer_index = ((reg & 0xf) - 1) & 7;
189	offset = amvdec_read_dos(core, MREG_FRAME_OFFSET);
190	amvdec_dst_buf_done_idx(sess, buffer_index, offset, field);
191
192end:
193	amvdec_write_dos(core, MREG_BUFFEROUT, 0);
194	return IRQ_HANDLED;
195}
196
197static irqreturn_t codec_mpeg12_isr(struct amvdec_session *sess)
198{
199	return IRQ_WAKE_THREAD;
200}
201
202struct amvdec_codec_ops codec_mpeg12_ops = {
203	.start = codec_mpeg12_start,
204	.stop = codec_mpeg12_stop,
205	.isr = codec_mpeg12_isr,
206	.threaded_isr = codec_mpeg12_threaded_isr,
207	.can_recycle = codec_mpeg12_can_recycle,
208	.recycle = codec_mpeg12_recycle,
209	.eos_sequence = codec_mpeg12_eos_sequence,
210};
211