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