tdmb_tsi_qc.c 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408
  1. /* drivers/media/tdmb/tdmb_qc_tsi.c
  2. *
  3. * Driver file for Qualcomm Transport Stream Interface
  4. *
  5. * Copyright (C) (2014, Samsung Electronics)
  6. *
  7. * This program is free software; you can redistribute it and/or modify
  8. * it under the terms of the GNU General Public License as published by
  9. * the Free Software Foundation version 2.
  10. *
  11. * This program is distributed "as is" WITHOUT ANY WARRANTY of any
  12. * kind, whether express or implied; without even the implied warranty
  13. * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. */
  16. #include <linux/init.h>
  17. #include <linux/vmalloc.h>
  18. #include <linux/slab.h>
  19. #include <linux/types.h>
  20. #include <mach/msm_tspp.h>
  21. #include "tdmb.h"
  22. #define CHANNEL_ID 0
  23. #define TSPP_CHANNEL_TIMEOUT 100 /* 100ms */
  24. #define TSPP_BUFFER_SIZE (20 * 1024) /* 20KB */
  25. #define TDMB_TS_SIZE 188
  26. struct tsi_pkt {
  27. struct list_head list;
  28. void *buf;
  29. u32 len;
  30. };
  31. struct tsi_dev {
  32. spinlock_t tsi_lock;
  33. struct list_head free_list;
  34. struct list_head full_list;
  35. int tsi_running;
  36. int filter_exists_flag;
  37. u8 packet_buff[TSPP_BUFFER_SIZE];
  38. };
  39. struct tsi_dev *tsi_priv;
  40. static void (*tsi_data_callback)(u8 *data, u32 length) = NULL;
  41. static void tdmb_tsi_pull_data(struct work_struct *work);
  42. static struct workqueue_struct *tdmb_tsi_workqueue;
  43. static DECLARE_WORK(tdmb_tsi_work, tdmb_tsi_pull_data);
  44. static struct tspp_select_source tdmb_tspp_set_source(void)
  45. {
  46. struct tspp_select_source tspp_source;
  47. tspp_source.clk_inverse = 0;
  48. tspp_source.data_inverse = 0;
  49. tspp_source.sync_inverse = 0;
  50. tspp_source.enable_inverse = 0;
  51. tspp_source.mode = TSPP_TSIF_MODE_1; /* whihout sync */
  52. tspp_source.source = TSPP_SOURCE_TSIF0;
  53. return tspp_source;
  54. }
  55. static struct tsi_pkt *tsi_get_pkt(struct tsi_dev *tsi, struct list_head *head)
  56. {
  57. unsigned long flags;
  58. struct tsi_pkt *pkt;
  59. spin_lock_irqsave(&tsi->tsi_lock, flags);
  60. if (list_empty(head)) {
  61. /* DPRINTK("TSI %p list is null\n", head); */
  62. spin_unlock_irqrestore(&tsi->tsi_lock, flags);
  63. return NULL;
  64. }
  65. pkt = list_first_entry(head, struct tsi_pkt, list);
  66. spin_unlock_irqrestore(&tsi->tsi_lock, flags);
  67. return pkt;
  68. }
  69. static int tsi_setup_bufs(struct list_head *head, int packet_cnt, u8 *pkt_buff)
  70. {
  71. struct tsi_pkt *pkt;
  72. u32 buf_size;
  73. u8 num_buf;
  74. int i;
  75. buf_size = TDMB_TS_SIZE * packet_cnt;
  76. num_buf = TSPP_BUFFER_SIZE / buf_size;
  77. for (i = 0; i < num_buf; i++) {
  78. pkt = kmalloc(sizeof(struct tsi_pkt), GFP_KERNEL);
  79. if (!pkt)
  80. return list_empty(head) ? -ENOMEM : 0 ;
  81. pkt->buf = (void *)(u8 *)(pkt_buff + i * buf_size);
  82. pkt->len = buf_size;
  83. list_add_tail(&pkt->list, head);
  84. }
  85. DPRINTK("total nodes calulated %d buf_size %d\n", num_buf, buf_size);
  86. return 0;
  87. }
  88. static void tsi_free_packets(struct tsi_dev *tsi)
  89. {
  90. struct tsi_pkt *pkt;
  91. unsigned long flags;
  92. struct list_head *full = &tsi->full_list;
  93. struct list_head *head = &(tsi->free_list);
  94. spin_lock_irqsave(&tsi->tsi_lock, flags);
  95. /* move all the packets from full list to free list */
  96. while (!list_empty(full)) {
  97. pkt = list_entry(full->next, struct tsi_pkt, list);
  98. list_move_tail(&pkt->list, &tsi->free_list);
  99. }
  100. spin_unlock_irqrestore(&tsi->tsi_lock, flags);
  101. while (!list_empty(head)) {
  102. pkt = list_entry(head->next, struct tsi_pkt, list);
  103. list_del(&pkt->list);
  104. kfree(pkt);
  105. }
  106. }
  107. static bool tdmb_tsi_create_workqueue(void)
  108. {
  109. tdmb_tsi_workqueue = create_singlethread_workqueue("ktdmbtsi");
  110. if (tdmb_tsi_workqueue)
  111. return true;
  112. else
  113. return false;
  114. }
  115. static bool tdmb_tsi_destroy_workqueue(void)
  116. {
  117. if (tdmb_tsi_workqueue) {
  118. flush_workqueue(tdmb_tsi_workqueue);
  119. destroy_workqueue(tdmb_tsi_workqueue);
  120. tdmb_tsi_workqueue = NULL;
  121. }
  122. return true;
  123. }
  124. static void tdmb_tsi_pull_data(struct work_struct *work)
  125. {
  126. struct tsi_pkt *pkt;
  127. unsigned long flags;
  128. if (!tsi_priv->tsi_running) {
  129. DPRINTK("%s : tsi_runing : %d\n",
  130. __func__, tsi_priv->tsi_running);
  131. return;
  132. }
  133. while ((pkt = tsi_get_pkt(tsi_priv, &tsi_priv->full_list)) != NULL) {
  134. #ifdef CONFIG_TSI_LIST_DEBUG
  135. DPRINTK("full_list virt:0x%p length:%d\n",pkt->buf, pkt->len);
  136. #endif
  137. if (tsi_data_callback)
  138. tsi_data_callback(pkt->buf, pkt->len);
  139. spin_lock_irqsave(&tsi_priv->tsi_lock, flags);
  140. list_move(&pkt->list, &tsi_priv->free_list);
  141. spin_unlock_irqrestore(&tsi_priv->tsi_lock, flags);
  142. }
  143. }
  144. static void tdmb_tspp_callback(int channel_id, void *user)
  145. {
  146. struct tsi_pkt *pkt;
  147. unsigned long flags;
  148. const struct tspp_data_descriptor *tspp_data_desc;
  149. if (!tsi_priv->tsi_running) {
  150. DPRINTK("%s : tsi_runing : %d\n",
  151. __func__, tsi_priv->tsi_running);
  152. return;
  153. }
  154. while ((tspp_data_desc = tspp_get_buffer(0, CHANNEL_ID)) != NULL) {
  155. pkt = tsi_get_pkt(tsi_priv, &tsi_priv->free_list);
  156. if (pkt == NULL) {
  157. DPRINTK("TSI..No more free bufs..\n");
  158. tspp_release_buffer(0, CHANNEL_ID, tspp_data_desc->id);
  159. return ;
  160. }
  161. if (tspp_data_desc->size == pkt->len)
  162. memcpy(pkt->buf, tspp_data_desc->virt_base, tspp_data_desc->size);
  163. else
  164. DPRINTK("Size err tspp_size:(%d) pkt_len:(%d) \n", \
  165. tspp_data_desc->size, pkt->len);
  166. spin_lock_irqsave(&tsi_priv->tsi_lock, flags);
  167. list_move_tail(&pkt->list, &tsi_priv->full_list);
  168. spin_unlock_irqrestore(&tsi_priv->tsi_lock, flags);
  169. tspp_release_buffer(0, CHANNEL_ID, tspp_data_desc->id);
  170. }
  171. if (tdmb_tsi_workqueue) {
  172. int ret;
  173. ret = queue_work(tdmb_tsi_workqueue, &tdmb_tsi_work);
  174. if (ret == 0)
  175. DPRINTK("failed in queue_work\n");
  176. }
  177. }
  178. static int tdmb_tspp_add_accept_all_filter(int channel_id,
  179. enum tspp_source source)
  180. {
  181. struct tspp_filter tspp_filter;
  182. int ret;
  183. if (tsi_priv->filter_exists_flag) {
  184. DPRINTK("%s: accept all filter already exists\n",
  185. __func__);
  186. return 0;
  187. }
  188. tspp_filter.priority = 15;
  189. tspp_filter.pid = 0;
  190. tspp_filter.mask = 0;
  191. tspp_filter.mode = TSPP_MODE_RAW_NO_SUFFIX;
  192. tspp_filter.source = source;
  193. tspp_filter.decrypt = 0;
  194. ret = tspp_add_filter(0, channel_id, &tspp_filter);
  195. if (!ret) {
  196. tsi_priv->filter_exists_flag = 1;
  197. DPRINTK("accept all filter added successfully\n");
  198. }
  199. return ret;
  200. }
  201. static int tdmb_tspp_remove_accept_all_filter(int channel_id,
  202. enum tspp_source source)
  203. {
  204. struct tspp_filter tspp_filter;
  205. int ret;
  206. if (tsi_priv->filter_exists_flag == 0) {
  207. DPRINTK("%s: accept all filter doesn't exist\n",
  208. __func__);
  209. return 0;
  210. }
  211. tspp_filter.priority = 15;
  212. ret = tspp_remove_filter(0, channel_id, &tspp_filter);
  213. if (!ret) {
  214. tsi_priv->filter_exists_flag = 0;
  215. DPRINTK("accept all filter removed successfully\n");
  216. }
  217. return ret;
  218. }
  219. int tdmb_tsi_start(void (*callback)(u8 *data, u32 length), int packet_cnt)
  220. {
  221. struct tspp_select_source tspp_source = tdmb_tspp_set_source();
  222. int ret = 0;
  223. if (tsi_priv->tsi_running)
  224. return -1;
  225. tsi_priv->tsi_running = 1;
  226. if (tsi_setup_bufs(&tsi_priv->free_list, packet_cnt, tsi_priv->packet_buff)) {
  227. DPRINTK("TSI failed to setup pkt list");
  228. ret = -ENXIO;
  229. return ret;
  230. }
  231. if (tdmb_tsi_create_workqueue() == false) {
  232. DPRINTK("tdmb_tsi_create_workqueue fail\n");
  233. ret = ENXIO;
  234. goto err_create_workqueue;
  235. }
  236. tsi_data_callback = callback;
  237. ret = tspp_open_channel(0, CHANNEL_ID);
  238. if (ret < 0) {
  239. DPRINTK("%s: tspp_open_channel(%d) failed (%d)\n",
  240. __func__,
  241. CHANNEL_ID,
  242. ret);
  243. goto err_open_channel;
  244. }
  245. ret = tspp_open_stream(0, CHANNEL_ID, &tspp_source);
  246. if (ret < 0) {
  247. DPRINTK("%s: tspp_select_source(%d,%d) failed (%d)\n",
  248. __func__,
  249. CHANNEL_ID,
  250. tspp_source.source,
  251. ret);
  252. goto err_open_stream;
  253. }
  254. ret = tspp_register_notification(0,
  255. CHANNEL_ID,
  256. tdmb_tspp_callback,
  257. NULL,
  258. TSPP_CHANNEL_TIMEOUT); /* 100ms */
  259. if (ret < 0) {
  260. DPRINTK(
  261. "%s: tspp_register_notification(%d) failed (%d)\n",
  262. __func__, CHANNEL_ID, ret);
  263. goto err_channel_unregister_notif;
  264. }
  265. ret = tspp_allocate_buffers(0,
  266. CHANNEL_ID,
  267. TSPP_BUFFER_SIZE / TDMB_TS_SIZE,
  268. TDMB_TS_SIZE * packet_cnt,
  269. packet_cnt, /* NOTIFICATION_SIZE */
  270. NULL, NULL, NULL);
  271. if (ret < 0) {
  272. DPRINTK(
  273. "%s: tspp_allocate_buffers(%d) failed (%d)\n",
  274. __func__, CHANNEL_ID, ret);
  275. goto err_allocate_buffers;
  276. }
  277. ret = tdmb_tspp_add_accept_all_filter(CHANNEL_ID,
  278. tspp_source.source);
  279. if (ret < 0) {
  280. DPRINTK(
  281. "%s: tdmb_tspp_add_accept_all_filter(%d, %d) failed\n",
  282. __func__, CHANNEL_ID, tspp_source.source);
  283. goto err_add_filter;
  284. }
  285. return ret;
  286. err_add_filter:
  287. err_allocate_buffers:
  288. tspp_unregister_notification(0, CHANNEL_ID);
  289. err_channel_unregister_notif:
  290. tspp_close_stream(0, CHANNEL_ID);
  291. err_open_stream:
  292. tspp_close_channel(0, CHANNEL_ID);
  293. err_open_channel:
  294. tdmb_tsi_destroy_workqueue();
  295. err_create_workqueue:
  296. tsi_free_packets(tsi_priv);
  297. tsi_priv->tsi_running = 0;
  298. return ret;
  299. }
  300. EXPORT_SYMBOL_GPL(tdmb_tsi_start);
  301. int tdmb_tsi_stop(void)
  302. {
  303. struct tspp_select_source tspp_source = tdmb_tspp_set_source();
  304. if (!tsi_priv->tsi_running)
  305. return -1;
  306. tsi_priv->tsi_running = 0;
  307. tdmb_tsi_destroy_workqueue();
  308. tsi_data_callback = NULL;
  309. tdmb_tspp_remove_accept_all_filter(CHANNEL_ID, tspp_source.source);
  310. tspp_unregister_notification(0, CHANNEL_ID);
  311. tspp_close_stream(0, CHANNEL_ID);
  312. tspp_close_channel(0, CHANNEL_ID);
  313. tsi_free_packets(tsi_priv);
  314. return 0;
  315. }
  316. EXPORT_SYMBOL_GPL(tdmb_tsi_stop);
  317. int tdmb_tsi_init(void)
  318. {
  319. tsi_priv = kmalloc(sizeof(struct tsi_dev), GFP_KERNEL);
  320. if (tsi_priv == NULL) {
  321. DPRINTK("NO Memory for tsi allocation\n");
  322. return -ENOMEM;
  323. }
  324. INIT_LIST_HEAD(&tsi_priv->full_list);
  325. INIT_LIST_HEAD(&tsi_priv->free_list);
  326. spin_lock_init(&tsi_priv->tsi_lock);
  327. tsi_priv->tsi_running = 0;
  328. tsi_priv->filter_exists_flag=0;
  329. return 0;
  330. }
  331. EXPORT_SYMBOL_GPL(tdmb_tsi_init);
  332. void tdmb_tsi_deinit(void)
  333. {
  334. kfree(tsi_priv);
  335. }
  336. EXPORT_SYMBOL_GPL(tdmb_tsi_deinit);