cx18-mailbox.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368
  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. #include <stdarg.h>
  22. #include "cx18-driver.h"
  23. #include "cx18-io.h"
  24. #include "cx18-scb.h"
  25. #include "cx18-irq.h"
  26. #include "cx18-mailbox.h"
  27. #define API_FAST (1 << 2) /* Short timeout */
  28. #define API_SLOW (1 << 3) /* Additional 300ms timeout */
  29. struct cx18_api_info {
  30. u32 cmd;
  31. u8 flags; /* Flags, see above */
  32. u8 rpu; /* Processing unit */
  33. const char *name; /* The name of the command */
  34. };
  35. #define API_ENTRY(rpu, x, f) { (x), (f), (rpu), #x }
  36. static const struct cx18_api_info api_info[] = {
  37. /* MPEG encoder API */
  38. API_ENTRY(CPU, CX18_CPU_SET_CHANNEL_TYPE, 0),
  39. API_ENTRY(CPU, CX18_EPU_DEBUG, 0),
  40. API_ENTRY(CPU, CX18_CREATE_TASK, 0),
  41. API_ENTRY(CPU, CX18_DESTROY_TASK, 0),
  42. API_ENTRY(CPU, CX18_CPU_CAPTURE_START, API_SLOW),
  43. API_ENTRY(CPU, CX18_CPU_CAPTURE_STOP, API_SLOW),
  44. API_ENTRY(CPU, CX18_CPU_CAPTURE_PAUSE, 0),
  45. API_ENTRY(CPU, CX18_CPU_CAPTURE_RESUME, 0),
  46. API_ENTRY(CPU, CX18_CPU_SET_CHANNEL_TYPE, 0),
  47. API_ENTRY(CPU, CX18_CPU_SET_STREAM_OUTPUT_TYPE, 0),
  48. API_ENTRY(CPU, CX18_CPU_SET_VIDEO_IN, 0),
  49. API_ENTRY(CPU, CX18_CPU_SET_VIDEO_RATE, 0),
  50. API_ENTRY(CPU, CX18_CPU_SET_VIDEO_RESOLUTION, 0),
  51. API_ENTRY(CPU, CX18_CPU_SET_FILTER_PARAM, 0),
  52. API_ENTRY(CPU, CX18_CPU_SET_SPATIAL_FILTER_TYPE, 0),
  53. API_ENTRY(CPU, CX18_CPU_SET_MEDIAN_CORING, 0),
  54. API_ENTRY(CPU, CX18_CPU_SET_INDEXTABLE, 0),
  55. API_ENTRY(CPU, CX18_CPU_SET_AUDIO_PARAMETERS, 0),
  56. API_ENTRY(CPU, CX18_CPU_SET_VIDEO_MUTE, 0),
  57. API_ENTRY(CPU, CX18_CPU_SET_AUDIO_MUTE, 0),
  58. API_ENTRY(CPU, CX18_CPU_SET_MISC_PARAMETERS, 0),
  59. API_ENTRY(CPU, CX18_CPU_SET_RAW_VBI_PARAM, API_SLOW),
  60. API_ENTRY(CPU, CX18_CPU_SET_CAPTURE_LINE_NO, 0),
  61. API_ENTRY(CPU, CX18_CPU_SET_COPYRIGHT, 0),
  62. API_ENTRY(CPU, CX18_CPU_SET_AUDIO_PID, 0),
  63. API_ENTRY(CPU, CX18_CPU_SET_VIDEO_PID, 0),
  64. API_ENTRY(CPU, CX18_CPU_SET_VER_CROP_LINE, 0),
  65. API_ENTRY(CPU, CX18_CPU_SET_GOP_STRUCTURE, 0),
  66. API_ENTRY(CPU, CX18_CPU_SET_SCENE_CHANGE_DETECTION, 0),
  67. API_ENTRY(CPU, CX18_CPU_SET_ASPECT_RATIO, 0),
  68. API_ENTRY(CPU, CX18_CPU_SET_SKIP_INPUT_FRAME, 0),
  69. API_ENTRY(CPU, CX18_CPU_SET_SLICED_VBI_PARAM, 0),
  70. API_ENTRY(CPU, CX18_CPU_SET_USERDATA_PLACE_HOLDER, 0),
  71. API_ENTRY(CPU, CX18_CPU_GET_ENC_PTS, 0),
  72. API_ENTRY(CPU, CX18_CPU_DE_SET_MDL_ACK, 0),
  73. API_ENTRY(CPU, CX18_CPU_DE_SET_MDL, API_FAST),
  74. API_ENTRY(CPU, CX18_APU_RESETAI, API_FAST),
  75. API_ENTRY(CPU, CX18_CPU_DE_RELEASE_MDL, API_SLOW),
  76. API_ENTRY(0, 0, 0),
  77. };
  78. static const struct cx18_api_info *find_api_info(u32 cmd)
  79. {
  80. int i;
  81. for (i = 0; api_info[i].cmd; i++)
  82. if (api_info[i].cmd == cmd)
  83. return &api_info[i];
  84. return NULL;
  85. }
  86. long cx18_mb_ack(struct cx18 *cx, const struct cx18_mailbox *mb, int rpu)
  87. {
  88. struct cx18_mailbox __iomem *ack_mb;
  89. u32 ack_irq;
  90. switch (rpu) {
  91. case APU:
  92. ack_irq = IRQ_EPU_TO_APU_ACK;
  93. ack_mb = &cx->scb->apu2epu_mb;
  94. break;
  95. case CPU:
  96. ack_irq = IRQ_EPU_TO_CPU_ACK;
  97. ack_mb = &cx->scb->cpu2epu_mb;
  98. break;
  99. default:
  100. CX18_WARN("Unhandled RPU (%d) for command %x ack\n",
  101. rpu, mb->cmd);
  102. return -EINVAL;
  103. }
  104. cx18_setup_page(cx, SCB_OFFSET);
  105. cx18_write_sync(cx, mb->request, &ack_mb->ack);
  106. cx18_write_reg_expect(cx, ack_irq, SW2_INT_SET, ack_irq, ack_irq);
  107. return 0;
  108. }
  109. static int cx18_api_call(struct cx18 *cx, u32 cmd, int args, u32 data[])
  110. {
  111. const struct cx18_api_info *info = find_api_info(cmd);
  112. u32 state, irq, req, ack, err;
  113. struct cx18_mailbox __iomem *mb;
  114. u32 __iomem *xpu_state;
  115. wait_queue_head_t *waitq;
  116. struct mutex *mb_lock;
  117. int timeout = 100;
  118. long unsigned int j, ret;
  119. int i;
  120. if (info == NULL) {
  121. CX18_WARN("unknown cmd %x\n", cmd);
  122. return -EINVAL;
  123. }
  124. if (cmd == CX18_CPU_DE_SET_MDL)
  125. CX18_DEBUG_HI_API("%s\n", info->name);
  126. else
  127. CX18_DEBUG_API("%s\n", info->name);
  128. switch (info->rpu) {
  129. case APU:
  130. waitq = &cx->mb_apu_waitq;
  131. mb_lock = &cx->epu2apu_mb_lock;
  132. irq = IRQ_EPU_TO_APU;
  133. mb = &cx->scb->epu2apu_mb;
  134. xpu_state = &cx->scb->apu_state;
  135. break;
  136. case CPU:
  137. waitq = &cx->mb_cpu_waitq;
  138. mb_lock = &cx->epu2cpu_mb_lock;
  139. irq = IRQ_EPU_TO_CPU;
  140. mb = &cx->scb->epu2cpu_mb;
  141. xpu_state = &cx->scb->cpu_state;
  142. break;
  143. default:
  144. CX18_WARN("Unknown RPU (%d) for API call\n", info->rpu);
  145. return -EINVAL;
  146. }
  147. mutex_lock(mb_lock);
  148. cx18_setup_page(cx, SCB_OFFSET);
  149. /*
  150. * Wait for an in-use mailbox to complete
  151. *
  152. * If the XPU is responding with Ack's, the mailbox shouldn't be in
  153. * a busy state, since we serialize access to it on our end.
  154. *
  155. * If the wait for ack after sending a previous command was interrupted
  156. * by a signal, we may get here and find a busy mailbox. After waiting,
  157. * mark it "not busy" from our end, if the XPU hasn't ack'ed it still.
  158. */
  159. state = cx18_readl(cx, xpu_state);
  160. req = cx18_readl(cx, &mb->request);
  161. j = msecs_to_jiffies(timeout);
  162. ret = wait_event_timeout(*waitq,
  163. (ack = cx18_readl(cx, &mb->ack)) == req,
  164. j);
  165. if (req != ack) {
  166. /* waited long enough, make the mbox "not busy" from our end */
  167. cx18_writel(cx, req, &mb->ack);
  168. CX18_ERR("mbox was found stuck busy when setting up for %s; "
  169. "clearing busy and trying to proceed\n", info->name);
  170. } else if (ret != j)
  171. CX18_DEBUG_API("waited %u usecs for busy mbox to be acked\n",
  172. jiffies_to_usecs(j-ret));
  173. /* Build the outgoing mailbox */
  174. req = ((req & 0xfffffffe) == 0xfffffffe) ? 1 : req + 1;
  175. cx18_writel(cx, cmd, &mb->cmd);
  176. for (i = 0; i < args; i++)
  177. cx18_writel(cx, data[i], &mb->args[i]);
  178. cx18_writel(cx, 0, &mb->error);
  179. cx18_writel(cx, req, &mb->request);
  180. cx18_writel(cx, req - 1, &mb->ack); /* ensure ack & req are distinct */
  181. /* Notify the XPU and wait for it to send an Ack back */
  182. if (info->flags & API_FAST)
  183. timeout /= 2;
  184. j = msecs_to_jiffies(timeout);
  185. CX18_DEBUG_HI_IRQ("sending interrupt SW1: %x to send %s\n",
  186. irq, info->name);
  187. cx18_write_reg_expect(cx, irq, SW1_INT_SET, irq, irq);
  188. ret = wait_event_interruptible_timeout(
  189. *waitq,
  190. cx18_readl(cx, &mb->ack) == cx18_readl(cx, &mb->request),
  191. j);
  192. if (ret == 0) {
  193. /* Timed out */
  194. mutex_unlock(mb_lock);
  195. CX18_ERR("sending %s timed out waiting for RPU "
  196. "acknowledgement\n", info->name);
  197. return -EINVAL;
  198. } else if (ret < 0) {
  199. /* Interrupted */
  200. mutex_unlock(mb_lock);
  201. CX18_WARN("sending %s was interrupted waiting for RPU"
  202. "acknowledgement\n", info->name);
  203. return -EINTR;
  204. } else if (ret != j)
  205. CX18_DEBUG_HI_API("waited %u usecs for %s to be acked\n",
  206. jiffies_to_usecs(j-ret), info->name);
  207. /* Collect data returned by the XPU */
  208. for (i = 0; i < MAX_MB_ARGUMENTS; i++)
  209. data[i] = cx18_readl(cx, &mb->args[i]);
  210. err = cx18_readl(cx, &mb->error);
  211. mutex_unlock(mb_lock);
  212. /*
  213. * Wait for XPU to perform extra actions for the caller in some cases.
  214. * e.g. CX18_CPU_DE_RELEASE_MDL will cause the CPU to send all buffers
  215. * back in a burst shortly thereafter
  216. */
  217. if (info->flags & API_SLOW)
  218. cx18_msleep_timeout(300, 0);
  219. if (err)
  220. CX18_DEBUG_API("mailbox error %08x for command %s\n", err,
  221. info->name);
  222. return err ? -EIO : 0;
  223. }
  224. int cx18_api(struct cx18 *cx, u32 cmd, int args, u32 data[])
  225. {
  226. return cx18_api_call(cx, cmd, args, data);
  227. }
  228. static int cx18_set_filter_param(struct cx18_stream *s)
  229. {
  230. struct cx18 *cx = s->cx;
  231. u32 mode;
  232. int ret;
  233. mode = (cx->filter_mode & 1) ? 2 : (cx->spatial_strength ? 1 : 0);
  234. ret = cx18_vapi(cx, CX18_CPU_SET_FILTER_PARAM, 4,
  235. s->handle, 1, mode, cx->spatial_strength);
  236. mode = (cx->filter_mode & 2) ? 2 : (cx->temporal_strength ? 1 : 0);
  237. ret = ret ? ret : cx18_vapi(cx, CX18_CPU_SET_FILTER_PARAM, 4,
  238. s->handle, 0, mode, cx->temporal_strength);
  239. ret = ret ? ret : cx18_vapi(cx, CX18_CPU_SET_FILTER_PARAM, 4,
  240. s->handle, 2, cx->filter_mode >> 2, 0);
  241. return ret;
  242. }
  243. int cx18_api_func(void *priv, u32 cmd, int in, int out,
  244. u32 data[CX2341X_MBOX_MAX_DATA])
  245. {
  246. struct cx18 *cx = priv;
  247. struct cx18_stream *s = &cx->streams[CX18_ENC_STREAM_TYPE_MPG];
  248. switch (cmd) {
  249. case CX2341X_ENC_SET_OUTPUT_PORT:
  250. return 0;
  251. case CX2341X_ENC_SET_FRAME_RATE:
  252. return cx18_vapi(cx, CX18_CPU_SET_VIDEO_IN, 6,
  253. s->handle, 0, 0, 0, 0, data[0]);
  254. case CX2341X_ENC_SET_FRAME_SIZE:
  255. return cx18_vapi(cx, CX18_CPU_SET_VIDEO_RESOLUTION, 3,
  256. s->handle, data[1], data[0]);
  257. case CX2341X_ENC_SET_STREAM_TYPE:
  258. return cx18_vapi(cx, CX18_CPU_SET_STREAM_OUTPUT_TYPE, 2,
  259. s->handle, data[0]);
  260. case CX2341X_ENC_SET_ASPECT_RATIO:
  261. return cx18_vapi(cx, CX18_CPU_SET_ASPECT_RATIO, 2,
  262. s->handle, data[0]);
  263. case CX2341X_ENC_SET_GOP_PROPERTIES:
  264. return cx18_vapi(cx, CX18_CPU_SET_GOP_STRUCTURE, 3,
  265. s->handle, data[0], data[1]);
  266. case CX2341X_ENC_SET_GOP_CLOSURE:
  267. return 0;
  268. case CX2341X_ENC_SET_AUDIO_PROPERTIES:
  269. return cx18_vapi(cx, CX18_CPU_SET_AUDIO_PARAMETERS, 2,
  270. s->handle, data[0]);
  271. case CX2341X_ENC_MUTE_AUDIO:
  272. return cx18_vapi(cx, CX18_CPU_SET_AUDIO_MUTE, 2,
  273. s->handle, data[0]);
  274. case CX2341X_ENC_SET_BIT_RATE:
  275. return cx18_vapi(cx, CX18_CPU_SET_VIDEO_RATE, 5,
  276. s->handle, data[0], data[1], data[2], data[3]);
  277. case CX2341X_ENC_MUTE_VIDEO:
  278. return cx18_vapi(cx, CX18_CPU_SET_VIDEO_MUTE, 2,
  279. s->handle, data[0]);
  280. case CX2341X_ENC_SET_FRAME_DROP_RATE:
  281. return cx18_vapi(cx, CX18_CPU_SET_SKIP_INPUT_FRAME, 2,
  282. s->handle, data[0]);
  283. case CX2341X_ENC_MISC:
  284. return cx18_vapi(cx, CX18_CPU_SET_MISC_PARAMETERS, 4,
  285. s->handle, data[0], data[1], data[2]);
  286. case CX2341X_ENC_SET_DNR_FILTER_MODE:
  287. cx->filter_mode = (data[0] & 3) | (data[1] << 2);
  288. return cx18_set_filter_param(s);
  289. case CX2341X_ENC_SET_DNR_FILTER_PROPS:
  290. cx->spatial_strength = data[0];
  291. cx->temporal_strength = data[1];
  292. return cx18_set_filter_param(s);
  293. case CX2341X_ENC_SET_SPATIAL_FILTER_TYPE:
  294. return cx18_vapi(cx, CX18_CPU_SET_SPATIAL_FILTER_TYPE, 3,
  295. s->handle, data[0], data[1]);
  296. case CX2341X_ENC_SET_CORING_LEVELS:
  297. return cx18_vapi(cx, CX18_CPU_SET_MEDIAN_CORING, 5,
  298. s->handle, data[0], data[1], data[2], data[3]);
  299. }
  300. CX18_WARN("Unknown cmd %x\n", cmd);
  301. return 0;
  302. }
  303. int cx18_vapi_result(struct cx18 *cx, u32 data[MAX_MB_ARGUMENTS],
  304. u32 cmd, int args, ...)
  305. {
  306. va_list ap;
  307. int i;
  308. va_start(ap, args);
  309. for (i = 0; i < args; i++)
  310. data[i] = va_arg(ap, u32);
  311. va_end(ap);
  312. return cx18_api(cx, cmd, args, data);
  313. }
  314. int cx18_vapi(struct cx18 *cx, u32 cmd, int args, ...)
  315. {
  316. u32 data[MAX_MB_ARGUMENTS];
  317. va_list ap;
  318. int i;
  319. if (cx == NULL) {
  320. CX18_ERR("cx == NULL (cmd=%x)\n", cmd);
  321. return 0;
  322. }
  323. if (args > MAX_MB_ARGUMENTS) {
  324. CX18_ERR("args too big (cmd=%x)\n", cmd);
  325. args = MAX_MB_ARGUMENTS;
  326. }
  327. va_start(ap, args);
  328. for (i = 0; i < args; i++)
  329. data[i] = va_arg(ap, u32);
  330. va_end(ap);
  331. return cx18_api(cx, cmd, args, data);
  332. }