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 
39 struct codec_mpeg12 {
40 	/* Buffer for the MPEG1/2 Workspace */
41 	void	  *workspace_vaddr;
42 	dma_addr_t workspace_paddr;
43 };
44 
45 static const u8 eos_sequence[SZ_1K] = { 0x00, 0x00, 0x01, 0xB7 };
46 
47 static const u8 *codec_mpeg12_eos_sequence(u32 *len)
48 {
49 	*len = ARRAY_SIZE(eos_sequence);
50 	return eos_sequence;
51 }
52 
53 static int codec_mpeg12_can_recycle(struct amvdec_core *core)
54 {
55 	return !amvdec_read_dos(core, MREG_BUFFERIN);
56 }
57 
58 static void codec_mpeg12_recycle(struct amvdec_core *core, u32 buf_idx)
59 {
60 	amvdec_write_dos(core, MREG_BUFFERIN, buf_idx + 1);
61 }
62 
63 static 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 
108 free_workspace:
109 	dma_free_coherent(core->dev, SIZE_WORKSPACE, mpeg12->workspace_vaddr,
110 			  mpeg12->workspace_paddr);
111 free_mpeg12:
112 	kfree(mpeg12);
113 
114 	return ret;
115 }
116 
117 static 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 
130 static 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 
153 static 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 
192 end:
193 	amvdec_write_dos(core, MREG_BUFFEROUT, 0);
194 	return IRQ_HANDLED;
195 }
196 
197 static irqreturn_t codec_mpeg12_isr(struct amvdec_session *sess)
198 {
199 	return IRQ_WAKE_THREAD;
200 }
201 
202 struct 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