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