cx18-mailbox.c revision b1526421eac9a912b2cda7e147f1da2aa31be278
1/*
2 *  cx18 mailbox functions
3 *
4 *  Copyright (C) 2007  Hans Verkuil <hverkuil@xs4all.nl>
5 *
6 *  This program is free software; you can redistribute it and/or modify
7 *  it under the terms of the GNU General Public License as published by
8 *  the Free Software Foundation; either version 2 of the License, or
9 *  (at your option) any later version.
10 *
11 *  This program is distributed in the hope that it will be useful,
12 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 *  GNU General Public License for more details.
15 *
16 *  You should have received a copy of the GNU General Public License
17 *  along with this program; if not, write to the Free Software
18 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
19 *  02111-1307  USA
20 */
21
22#include <stdarg.h>
23
24#include "cx18-driver.h"
25#include "cx18-io.h"
26#include "cx18-scb.h"
27#include "cx18-irq.h"
28#include "cx18-mailbox.h"
29
30#define API_FAST (1 << 2) /* Short timeout */
31#define API_SLOW (1 << 3) /* Additional 300ms timeout */
32
33#define APU 0
34#define CPU 1
35#define EPU 2
36#define HPU 3
37
38struct cx18_api_info {
39	u32 cmd;
40	u8 flags;		/* Flags, see above */
41	u8 rpu;			/* Processing unit */
42	const char *name; 	/* The name of the command */
43};
44
45#define API_ENTRY(rpu, x, f) { (x), (f), (rpu), #x }
46
47static const struct cx18_api_info api_info[] = {
48	/* MPEG encoder API */
49	API_ENTRY(CPU, CX18_CPU_SET_CHANNEL_TYPE,		0),
50	API_ENTRY(CPU, CX18_EPU_DEBUG, 				0),
51	API_ENTRY(CPU, CX18_CREATE_TASK, 			0),
52	API_ENTRY(CPU, CX18_DESTROY_TASK, 			0),
53	API_ENTRY(CPU, CX18_CPU_CAPTURE_START,                  API_SLOW),
54	API_ENTRY(CPU, CX18_CPU_CAPTURE_STOP,                   API_SLOW),
55	API_ENTRY(CPU, CX18_CPU_CAPTURE_PAUSE,                  0),
56	API_ENTRY(CPU, CX18_CPU_CAPTURE_RESUME,                 0),
57	API_ENTRY(CPU, CX18_CPU_SET_CHANNEL_TYPE,               0),
58	API_ENTRY(CPU, CX18_CPU_SET_STREAM_OUTPUT_TYPE,         0),
59	API_ENTRY(CPU, CX18_CPU_SET_VIDEO_IN,                   0),
60	API_ENTRY(CPU, CX18_CPU_SET_VIDEO_RATE,                 0),
61	API_ENTRY(CPU, CX18_CPU_SET_VIDEO_RESOLUTION,           0),
62	API_ENTRY(CPU, CX18_CPU_SET_FILTER_PARAM,               0),
63	API_ENTRY(CPU, CX18_CPU_SET_SPATIAL_FILTER_TYPE,        0),
64	API_ENTRY(CPU, CX18_CPU_SET_MEDIAN_CORING,              0),
65	API_ENTRY(CPU, CX18_CPU_SET_INDEXTABLE,                 0),
66	API_ENTRY(CPU, CX18_CPU_SET_AUDIO_PARAMETERS,           0),
67	API_ENTRY(CPU, CX18_CPU_SET_VIDEO_MUTE,                 0),
68	API_ENTRY(CPU, CX18_CPU_SET_AUDIO_MUTE,                 0),
69	API_ENTRY(CPU, CX18_CPU_SET_MISC_PARAMETERS,            0),
70	API_ENTRY(CPU, CX18_CPU_SET_RAW_VBI_PARAM,              API_SLOW),
71	API_ENTRY(CPU, CX18_CPU_SET_CAPTURE_LINE_NO,            0),
72	API_ENTRY(CPU, CX18_CPU_SET_COPYRIGHT,                  0),
73	API_ENTRY(CPU, CX18_CPU_SET_AUDIO_PID,                  0),
74	API_ENTRY(CPU, CX18_CPU_SET_VIDEO_PID,                  0),
75	API_ENTRY(CPU, CX18_CPU_SET_VER_CROP_LINE,              0),
76	API_ENTRY(CPU, CX18_CPU_SET_GOP_STRUCTURE,              0),
77	API_ENTRY(CPU, CX18_CPU_SET_SCENE_CHANGE_DETECTION,     0),
78	API_ENTRY(CPU, CX18_CPU_SET_ASPECT_RATIO,               0),
79	API_ENTRY(CPU, CX18_CPU_SET_SKIP_INPUT_FRAME,           0),
80	API_ENTRY(CPU, CX18_CPU_SET_SLICED_VBI_PARAM,           0),
81	API_ENTRY(CPU, CX18_CPU_SET_USERDATA_PLACE_HOLDER,      0),
82	API_ENTRY(CPU, CX18_CPU_GET_ENC_PTS,                    0),
83	API_ENTRY(CPU, CX18_CPU_DE_SET_MDL_ACK,			0),
84	API_ENTRY(CPU, CX18_CPU_DE_SET_MDL,			API_FAST),
85	API_ENTRY(CPU, CX18_APU_RESETAI,			API_FAST),
86	API_ENTRY(CPU, CX18_CPU_DE_RELEASE_MDL,			0),
87	API_ENTRY(0, 0,						0),
88};
89
90static const struct cx18_api_info *find_api_info(u32 cmd)
91{
92	int i;
93
94	for (i = 0; api_info[i].cmd; i++)
95		if (api_info[i].cmd == cmd)
96			return &api_info[i];
97	return NULL;
98}
99
100static struct cx18_mailbox __iomem *cx18_mb_is_complete(struct cx18 *cx, int rpu,
101		u32 *state, u32 *irq, u32 *req)
102{
103	struct cx18_mailbox __iomem *mb = NULL;
104	int wait_count = 0;
105	u32 ack;
106
107	switch (rpu) {
108	case APU:
109		mb = &cx->scb->epu2apu_mb;
110		*state = cx18_readl(cx, &cx->scb->apu_state);
111		*irq = cx18_readl(cx, &cx->scb->epu2apu_irq);
112		break;
113
114	case CPU:
115		mb = &cx->scb->epu2cpu_mb;
116		*state = cx18_readl(cx, &cx->scb->cpu_state);
117		*irq = cx18_readl(cx, &cx->scb->epu2cpu_irq);
118		break;
119
120	case HPU:
121		mb = &cx->scb->epu2hpu_mb;
122		*state = cx18_readl(cx, &cx->scb->hpu_state);
123		*irq = cx18_readl(cx, &cx->scb->epu2hpu_irq);
124		break;
125	}
126
127	if (mb == NULL)
128		return mb;
129
130	do {
131		*req = cx18_readl(cx, &mb->request);
132		ack = cx18_readl(cx, &mb->ack);
133		wait_count++;
134	} while (*req != ack && wait_count < 600);
135
136	if (*req == ack) {
137		(*req)++;
138		if (*req == 0 || *req == 0xffffffff)
139			*req = 1;
140		return mb;
141	}
142	return NULL;
143}
144
145long cx18_mb_ack(struct cx18 *cx, const struct cx18_mailbox *mb)
146{
147	const struct cx18_api_info *info = find_api_info(mb->cmd);
148	struct cx18_mailbox __iomem *ack_mb;
149	u32 ack_irq;
150	u8 rpu = CPU;
151
152	if (info == NULL && mb->cmd) {
153		CX18_WARN("Cannot ack unknown command %x\n", mb->cmd);
154		return -EINVAL;
155	}
156	if (info)
157		rpu = info->rpu;
158
159	switch (rpu) {
160	case HPU:
161		ack_irq = IRQ_EPU_TO_HPU_ACK;
162		ack_mb = &cx->scb->hpu2epu_mb;
163		break;
164	case APU:
165		ack_irq = IRQ_EPU_TO_APU_ACK;
166		ack_mb = &cx->scb->apu2epu_mb;
167		break;
168	case CPU:
169		ack_irq = IRQ_EPU_TO_CPU_ACK;
170		ack_mb = &cx->scb->cpu2epu_mb;
171		break;
172	default:
173		CX18_WARN("Unknown RPU for command %x\n", mb->cmd);
174		return -EINVAL;
175	}
176
177	cx18_setup_page(cx, SCB_OFFSET);
178	cx18_write_sync(cx, mb->request, &ack_mb->ack);
179	cx18_write_reg(cx, ack_irq, SW2_INT_SET);
180	return 0;
181}
182
183
184static int cx18_api_call(struct cx18 *cx, u32 cmd, int args, u32 data[])
185{
186	const struct cx18_api_info *info = find_api_info(cmd);
187	u32 state = 0, irq = 0, req, oldreq, err;
188	struct cx18_mailbox __iomem *mb;
189	wait_queue_head_t *waitq;
190	int timeout = 100;
191	int cnt = 0;
192	int sig = 0;
193	int i;
194
195	if (info == NULL) {
196		CX18_WARN("unknown cmd %x\n", cmd);
197		return -EINVAL;
198	}
199
200	if (cmd == CX18_CPU_DE_SET_MDL)
201		CX18_DEBUG_HI_API("%s\n", info->name);
202	else
203		CX18_DEBUG_API("%s\n", info->name);
204	cx18_setup_page(cx, SCB_OFFSET);
205	mb = cx18_mb_is_complete(cx, info->rpu, &state, &irq, &req);
206
207	if (mb == NULL) {
208		CX18_ERR("mb %s busy\n", info->name);
209		return -EBUSY;
210	}
211
212	oldreq = req - 1;
213	cx18_writel(cx, cmd, &mb->cmd);
214	for (i = 0; i < args; i++)
215		cx18_writel(cx, data[i], &mb->args[i]);
216	cx18_writel(cx, 0, &mb->error);
217	cx18_writel(cx, req, &mb->request);
218
219	switch (info->rpu) {
220	case APU: waitq = &cx->mb_apu_waitq; break;
221	case CPU: waitq = &cx->mb_cpu_waitq; break;
222	case EPU: waitq = &cx->mb_epu_waitq; break;
223	case HPU: waitq = &cx->mb_hpu_waitq; break;
224	default: return -EINVAL;
225	}
226	if (info->flags & API_FAST)
227		timeout /= 2;
228	cx18_write_reg(cx, irq, SW1_INT_SET);
229
230	while (!sig && cx18_readl(cx, &mb->ack) != cx18_readl(cx, &mb->request)
231	       && cnt < 660) {
232		if (cnt > 200 && !in_atomic())
233			sig = cx18_msleep_timeout(10, 1);
234		cnt++;
235	}
236	if (sig)
237		return -EINTR;
238	if (cnt == 660) {
239		cx18_writel(cx, oldreq, &mb->request);
240		CX18_ERR("mb %s failed\n", info->name);
241		return -EINVAL;
242	}
243	for (i = 0; i < MAX_MB_ARGUMENTS; i++)
244		data[i] = cx18_readl(cx, &mb->args[i]);
245	err = cx18_readl(cx, &mb->error);
246	if (!in_atomic() && (info->flags & API_SLOW))
247		cx18_msleep_timeout(300, 0);
248	if (err)
249		CX18_DEBUG_API("mailbox error %08x for command %s\n", err,
250				info->name);
251	return err ? -EIO : 0;
252}
253
254int cx18_api(struct cx18 *cx, u32 cmd, int args, u32 data[])
255{
256	int res = cx18_api_call(cx, cmd, args, data);
257
258	/* Allow a single retry, probably already too late though.
259	   If there is no free mailbox then that is usually an indication
260	   of a more serious problem. */
261	return (res == -EBUSY) ? cx18_api_call(cx, cmd, args, data) : res;
262}
263
264static int cx18_set_filter_param(struct cx18_stream *s)
265{
266	struct cx18 *cx = s->cx;
267	u32 mode;
268	int ret;
269
270	mode = (cx->filter_mode & 1) ? 2 : (cx->spatial_strength ? 1 : 0);
271	ret = cx18_vapi(cx, CX18_CPU_SET_FILTER_PARAM, 4,
272			s->handle, 1, mode, cx->spatial_strength);
273	mode = (cx->filter_mode & 2) ? 2 : (cx->temporal_strength ? 1 : 0);
274	ret = ret ? ret : cx18_vapi(cx, CX18_CPU_SET_FILTER_PARAM, 4,
275			s->handle, 0, mode, cx->temporal_strength);
276	ret = ret ? ret : cx18_vapi(cx, CX18_CPU_SET_FILTER_PARAM, 4,
277			s->handle, 2, cx->filter_mode >> 2, 0);
278	return ret;
279}
280
281int cx18_api_func(void *priv, u32 cmd, int in, int out,
282		u32 data[CX2341X_MBOX_MAX_DATA])
283{
284	struct cx18 *cx = priv;
285	struct cx18_stream *s = &cx->streams[CX18_ENC_STREAM_TYPE_MPG];
286
287	switch (cmd) {
288	case CX2341X_ENC_SET_OUTPUT_PORT:
289		return 0;
290	case CX2341X_ENC_SET_FRAME_RATE:
291		return cx18_vapi(cx, CX18_CPU_SET_VIDEO_IN, 6,
292				s->handle, 0, 0, 0, 0, data[0]);
293	case CX2341X_ENC_SET_FRAME_SIZE:
294		return cx18_vapi(cx, CX18_CPU_SET_VIDEO_RESOLUTION, 3,
295				s->handle, data[1], data[0]);
296	case CX2341X_ENC_SET_STREAM_TYPE:
297		return cx18_vapi(cx, CX18_CPU_SET_STREAM_OUTPUT_TYPE, 2,
298				s->handle, data[0]);
299	case CX2341X_ENC_SET_ASPECT_RATIO:
300		return cx18_vapi(cx, CX18_CPU_SET_ASPECT_RATIO, 2,
301				s->handle, data[0]);
302
303	case CX2341X_ENC_SET_GOP_PROPERTIES:
304		return cx18_vapi(cx, CX18_CPU_SET_GOP_STRUCTURE, 3,
305				s->handle, data[0], data[1]);
306	case CX2341X_ENC_SET_GOP_CLOSURE:
307		return 0;
308	case CX2341X_ENC_SET_AUDIO_PROPERTIES:
309		return cx18_vapi(cx, CX18_CPU_SET_AUDIO_PARAMETERS, 2,
310				s->handle, data[0]);
311	case CX2341X_ENC_MUTE_AUDIO:
312		return cx18_vapi(cx, CX18_CPU_SET_AUDIO_MUTE, 2,
313				s->handle, data[0]);
314	case CX2341X_ENC_SET_BIT_RATE:
315		return cx18_vapi(cx, CX18_CPU_SET_VIDEO_RATE, 5,
316				s->handle, data[0], data[1], data[2], data[3]);
317	case CX2341X_ENC_MUTE_VIDEO:
318		return cx18_vapi(cx, CX18_CPU_SET_VIDEO_MUTE, 2,
319				s->handle, data[0]);
320	case CX2341X_ENC_SET_FRAME_DROP_RATE:
321		return cx18_vapi(cx, CX18_CPU_SET_SKIP_INPUT_FRAME, 2,
322				s->handle, data[0]);
323	case CX2341X_ENC_MISC:
324		return cx18_vapi(cx, CX18_CPU_SET_MISC_PARAMETERS, 4,
325				s->handle, data[0], data[1], data[2]);
326	case CX2341X_ENC_SET_DNR_FILTER_MODE:
327		cx->filter_mode = (data[0] & 3) | (data[1] << 2);
328		return cx18_set_filter_param(s);
329	case CX2341X_ENC_SET_DNR_FILTER_PROPS:
330		cx->spatial_strength = data[0];
331		cx->temporal_strength = data[1];
332		return cx18_set_filter_param(s);
333	case CX2341X_ENC_SET_SPATIAL_FILTER_TYPE:
334		return cx18_vapi(cx, CX18_CPU_SET_SPATIAL_FILTER_TYPE, 3,
335				s->handle, data[0], data[1]);
336	case CX2341X_ENC_SET_CORING_LEVELS:
337		return cx18_vapi(cx, CX18_CPU_SET_MEDIAN_CORING, 5,
338				s->handle, data[0], data[1], data[2], data[3]);
339	}
340	CX18_WARN("Unknown cmd %x\n", cmd);
341	return 0;
342}
343
344int cx18_vapi_result(struct cx18 *cx, u32 data[MAX_MB_ARGUMENTS],
345		u32 cmd, int args, ...)
346{
347	va_list ap;
348	int i;
349
350	va_start(ap, args);
351	for (i = 0; i < args; i++)
352		data[i] = va_arg(ap, u32);
353	va_end(ap);
354	return cx18_api(cx, cmd, args, data);
355}
356
357int cx18_vapi(struct cx18 *cx, u32 cmd, int args, ...)
358{
359	u32 data[MAX_MB_ARGUMENTS];
360	va_list ap;
361	int i;
362
363	if (cx == NULL) {
364		CX18_ERR("cx == NULL (cmd=%x)\n", cmd);
365		return 0;
366	}
367	if (args > MAX_MB_ARGUMENTS) {
368		CX18_ERR("args too big (cmd=%x)\n", cmd);
369		args = MAX_MB_ARGUMENTS;
370	}
371	va_start(ap, args);
372	for (i = 0; i < args; i++)
373		data[i] = va_arg(ap, u32);
374	va_end(ap);
375	return cx18_api(cx, cmd, args, data);
376}
377