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