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