123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408 |
- /* drivers/media/tdmb/tdmb_qc_tsi.c
- *
- * Driver file for Qualcomm Transport Stream Interface
- *
- * Copyright (C) (2014, Samsung Electronics)
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation version 2.
- *
- * This program is distributed "as is" WITHOUT ANY WARRANTY of any
- * kind, whether express or implied; without even the implied warranty
- * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
- #include <linux/init.h>
- #include <linux/vmalloc.h>
- #include <linux/slab.h>
- #include <linux/types.h>
- #include <mach/msm_tspp.h>
- #include "tdmb.h"
- #define CHANNEL_ID 0
- #define TSPP_CHANNEL_TIMEOUT 100 /* 100ms */
- #define TSPP_BUFFER_SIZE (20 * 1024) /* 20KB */
- #define TDMB_TS_SIZE 188
- struct tsi_pkt {
- struct list_head list;
- void *buf;
- u32 len;
- };
- struct tsi_dev {
- spinlock_t tsi_lock;
- struct list_head free_list;
- struct list_head full_list;
- int tsi_running;
- int filter_exists_flag;
- u8 packet_buff[TSPP_BUFFER_SIZE];
- };
- struct tsi_dev *tsi_priv;
- static void (*tsi_data_callback)(u8 *data, u32 length) = NULL;
- static void tdmb_tsi_pull_data(struct work_struct *work);
- static struct workqueue_struct *tdmb_tsi_workqueue;
- static DECLARE_WORK(tdmb_tsi_work, tdmb_tsi_pull_data);
- static struct tspp_select_source tdmb_tspp_set_source(void)
- {
- struct tspp_select_source tspp_source;
- tspp_source.clk_inverse = 0;
- tspp_source.data_inverse = 0;
- tspp_source.sync_inverse = 0;
- tspp_source.enable_inverse = 0;
- tspp_source.mode = TSPP_TSIF_MODE_1; /* whihout sync */
- tspp_source.source = TSPP_SOURCE_TSIF0;
- return tspp_source;
- }
- static struct tsi_pkt *tsi_get_pkt(struct tsi_dev *tsi, struct list_head *head)
- {
- unsigned long flags;
- struct tsi_pkt *pkt;
- spin_lock_irqsave(&tsi->tsi_lock, flags);
- if (list_empty(head)) {
- /* DPRINTK("TSI %p list is null\n", head); */
- spin_unlock_irqrestore(&tsi->tsi_lock, flags);
- return NULL;
- }
- pkt = list_first_entry(head, struct tsi_pkt, list);
- spin_unlock_irqrestore(&tsi->tsi_lock, flags);
- return pkt;
- }
- static int tsi_setup_bufs(struct list_head *head, int packet_cnt, u8 *pkt_buff)
- {
- struct tsi_pkt *pkt;
- u32 buf_size;
- u8 num_buf;
- int i;
- buf_size = TDMB_TS_SIZE * packet_cnt;
- num_buf = TSPP_BUFFER_SIZE / buf_size;
- for (i = 0; i < num_buf; i++) {
- pkt = kmalloc(sizeof(struct tsi_pkt), GFP_KERNEL);
- if (!pkt)
- return list_empty(head) ? -ENOMEM : 0 ;
- pkt->buf = (void *)(u8 *)(pkt_buff + i * buf_size);
- pkt->len = buf_size;
- list_add_tail(&pkt->list, head);
- }
- DPRINTK("total nodes calulated %d buf_size %d\n", num_buf, buf_size);
- return 0;
- }
- static void tsi_free_packets(struct tsi_dev *tsi)
- {
- struct tsi_pkt *pkt;
- unsigned long flags;
- struct list_head *full = &tsi->full_list;
- struct list_head *head = &(tsi->free_list);
- spin_lock_irqsave(&tsi->tsi_lock, flags);
- /* move all the packets from full list to free list */
- while (!list_empty(full)) {
- pkt = list_entry(full->next, struct tsi_pkt, list);
- list_move_tail(&pkt->list, &tsi->free_list);
- }
- spin_unlock_irqrestore(&tsi->tsi_lock, flags);
- while (!list_empty(head)) {
- pkt = list_entry(head->next, struct tsi_pkt, list);
- list_del(&pkt->list);
- kfree(pkt);
- }
- }
- static bool tdmb_tsi_create_workqueue(void)
- {
- tdmb_tsi_workqueue = create_singlethread_workqueue("ktdmbtsi");
- if (tdmb_tsi_workqueue)
- return true;
- else
- return false;
- }
- static bool tdmb_tsi_destroy_workqueue(void)
- {
- if (tdmb_tsi_workqueue) {
- flush_workqueue(tdmb_tsi_workqueue);
- destroy_workqueue(tdmb_tsi_workqueue);
- tdmb_tsi_workqueue = NULL;
- }
- return true;
- }
- static void tdmb_tsi_pull_data(struct work_struct *work)
- {
- struct tsi_pkt *pkt;
- unsigned long flags;
- if (!tsi_priv->tsi_running) {
- DPRINTK("%s : tsi_runing : %d\n",
- __func__, tsi_priv->tsi_running);
- return;
- }
- while ((pkt = tsi_get_pkt(tsi_priv, &tsi_priv->full_list)) != NULL) {
- #ifdef CONFIG_TSI_LIST_DEBUG
- DPRINTK("full_list virt:0x%p length:%d\n",pkt->buf, pkt->len);
- #endif
- if (tsi_data_callback)
- tsi_data_callback(pkt->buf, pkt->len);
- spin_lock_irqsave(&tsi_priv->tsi_lock, flags);
- list_move(&pkt->list, &tsi_priv->free_list);
- spin_unlock_irqrestore(&tsi_priv->tsi_lock, flags);
- }
- }
- static void tdmb_tspp_callback(int channel_id, void *user)
- {
- struct tsi_pkt *pkt;
- unsigned long flags;
- const struct tspp_data_descriptor *tspp_data_desc;
- if (!tsi_priv->tsi_running) {
- DPRINTK("%s : tsi_runing : %d\n",
- __func__, tsi_priv->tsi_running);
- return;
- }
- while ((tspp_data_desc = tspp_get_buffer(0, CHANNEL_ID)) != NULL) {
- pkt = tsi_get_pkt(tsi_priv, &tsi_priv->free_list);
- if (pkt == NULL) {
- DPRINTK("TSI..No more free bufs..\n");
- tspp_release_buffer(0, CHANNEL_ID, tspp_data_desc->id);
- return ;
- }
- if (tspp_data_desc->size == pkt->len)
- memcpy(pkt->buf, tspp_data_desc->virt_base, tspp_data_desc->size);
- else
- DPRINTK("Size err tspp_size:(%d) pkt_len:(%d) \n", \
- tspp_data_desc->size, pkt->len);
- spin_lock_irqsave(&tsi_priv->tsi_lock, flags);
- list_move_tail(&pkt->list, &tsi_priv->full_list);
- spin_unlock_irqrestore(&tsi_priv->tsi_lock, flags);
- tspp_release_buffer(0, CHANNEL_ID, tspp_data_desc->id);
- }
- if (tdmb_tsi_workqueue) {
- int ret;
- ret = queue_work(tdmb_tsi_workqueue, &tdmb_tsi_work);
- if (ret == 0)
- DPRINTK("failed in queue_work\n");
- }
- }
- static int tdmb_tspp_add_accept_all_filter(int channel_id,
- enum tspp_source source)
- {
- struct tspp_filter tspp_filter;
- int ret;
- if (tsi_priv->filter_exists_flag) {
- DPRINTK("%s: accept all filter already exists\n",
- __func__);
- return 0;
- }
- tspp_filter.priority = 15;
- tspp_filter.pid = 0;
- tspp_filter.mask = 0;
- tspp_filter.mode = TSPP_MODE_RAW_NO_SUFFIX;
- tspp_filter.source = source;
- tspp_filter.decrypt = 0;
- ret = tspp_add_filter(0, channel_id, &tspp_filter);
- if (!ret) {
- tsi_priv->filter_exists_flag = 1;
- DPRINTK("accept all filter added successfully\n");
- }
- return ret;
- }
- static int tdmb_tspp_remove_accept_all_filter(int channel_id,
- enum tspp_source source)
- {
- struct tspp_filter tspp_filter;
- int ret;
- if (tsi_priv->filter_exists_flag == 0) {
- DPRINTK("%s: accept all filter doesn't exist\n",
- __func__);
- return 0;
- }
- tspp_filter.priority = 15;
- ret = tspp_remove_filter(0, channel_id, &tspp_filter);
- if (!ret) {
- tsi_priv->filter_exists_flag = 0;
- DPRINTK("accept all filter removed successfully\n");
- }
- return ret;
- }
- int tdmb_tsi_start(void (*callback)(u8 *data, u32 length), int packet_cnt)
- {
- struct tspp_select_source tspp_source = tdmb_tspp_set_source();
- int ret = 0;
- if (tsi_priv->tsi_running)
- return -1;
- tsi_priv->tsi_running = 1;
- if (tsi_setup_bufs(&tsi_priv->free_list, packet_cnt, tsi_priv->packet_buff)) {
- DPRINTK("TSI failed to setup pkt list");
- ret = -ENXIO;
- return ret;
- }
- if (tdmb_tsi_create_workqueue() == false) {
- DPRINTK("tdmb_tsi_create_workqueue fail\n");
- ret = ENXIO;
- goto err_create_workqueue;
- }
- tsi_data_callback = callback;
- ret = tspp_open_channel(0, CHANNEL_ID);
- if (ret < 0) {
- DPRINTK("%s: tspp_open_channel(%d) failed (%d)\n",
- __func__,
- CHANNEL_ID,
- ret);
- goto err_open_channel;
- }
- ret = tspp_open_stream(0, CHANNEL_ID, &tspp_source);
- if (ret < 0) {
- DPRINTK("%s: tspp_select_source(%d,%d) failed (%d)\n",
- __func__,
- CHANNEL_ID,
- tspp_source.source,
- ret);
- goto err_open_stream;
- }
- ret = tspp_register_notification(0,
- CHANNEL_ID,
- tdmb_tspp_callback,
- NULL,
- TSPP_CHANNEL_TIMEOUT); /* 100ms */
- if (ret < 0) {
- DPRINTK(
- "%s: tspp_register_notification(%d) failed (%d)\n",
- __func__, CHANNEL_ID, ret);
- goto err_channel_unregister_notif;
- }
- ret = tspp_allocate_buffers(0,
- CHANNEL_ID,
- TSPP_BUFFER_SIZE / TDMB_TS_SIZE,
- TDMB_TS_SIZE * packet_cnt,
- packet_cnt, /* NOTIFICATION_SIZE */
- NULL, NULL, NULL);
- if (ret < 0) {
- DPRINTK(
- "%s: tspp_allocate_buffers(%d) failed (%d)\n",
- __func__, CHANNEL_ID, ret);
- goto err_allocate_buffers;
- }
- ret = tdmb_tspp_add_accept_all_filter(CHANNEL_ID,
- tspp_source.source);
- if (ret < 0) {
- DPRINTK(
- "%s: tdmb_tspp_add_accept_all_filter(%d, %d) failed\n",
- __func__, CHANNEL_ID, tspp_source.source);
- goto err_add_filter;
- }
- return ret;
- err_add_filter:
- err_allocate_buffers:
- tspp_unregister_notification(0, CHANNEL_ID);
- err_channel_unregister_notif:
- tspp_close_stream(0, CHANNEL_ID);
- err_open_stream:
- tspp_close_channel(0, CHANNEL_ID);
- err_open_channel:
- tdmb_tsi_destroy_workqueue();
- err_create_workqueue:
- tsi_free_packets(tsi_priv);
- tsi_priv->tsi_running = 0;
- return ret;
- }
- EXPORT_SYMBOL_GPL(tdmb_tsi_start);
- int tdmb_tsi_stop(void)
- {
- struct tspp_select_source tspp_source = tdmb_tspp_set_source();
- if (!tsi_priv->tsi_running)
- return -1;
- tsi_priv->tsi_running = 0;
- tdmb_tsi_destroy_workqueue();
- tsi_data_callback = NULL;
- tdmb_tspp_remove_accept_all_filter(CHANNEL_ID, tspp_source.source);
- tspp_unregister_notification(0, CHANNEL_ID);
- tspp_close_stream(0, CHANNEL_ID);
- tspp_close_channel(0, CHANNEL_ID);
- tsi_free_packets(tsi_priv);
- return 0;
- }
- EXPORT_SYMBOL_GPL(tdmb_tsi_stop);
- int tdmb_tsi_init(void)
- {
- tsi_priv = kmalloc(sizeof(struct tsi_dev), GFP_KERNEL);
- if (tsi_priv == NULL) {
- DPRINTK("NO Memory for tsi allocation\n");
- return -ENOMEM;
- }
- INIT_LIST_HEAD(&tsi_priv->full_list);
- INIT_LIST_HEAD(&tsi_priv->free_list);
- spin_lock_init(&tsi_priv->tsi_lock);
- tsi_priv->tsi_running = 0;
- tsi_priv->filter_exists_flag=0;
- return 0;
- }
- EXPORT_SYMBOL_GPL(tdmb_tsi_init);
- void tdmb_tsi_deinit(void)
- {
- kfree(tsi_priv);
- }
- EXPORT_SYMBOL_GPL(tdmb_tsi_deinit);
|