1 /* linux/drivers/media/video/samsung/s3c-tsi.c
3 * Driver file for Samsung Transport Stream Interface
5 * Copyright (c) 2009 Samsung Electronics
6 * http://www.samsungsemi.com/
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License version 2 as
10 * published by the Free Software Foundation.
12 #include <linux/version.h>
13 #include <linux/module.h>
15 #include <linux/uaccess.h>
16 #include <linux/interrupt.h>
17 #include <linux/init.h>
18 #include <linux/miscdevice.h>
19 #include <linux/clk.h>
20 #include <linux/platform_device.h>
21 #include <linux/dma-mapping.h>
24 #include <mach/irqs.h>
25 #include <mach/gpio.h>
26 #if defined(CONFIG_CPU_S5PV210) || defined(CONFIG_TARGET_LOCALE_NTT)
28 #include <mach/regs-clock.h>
29 #include <mach/regs-tsi.h>
32 #include <plat/regs-clock.h>
33 #include <plat/regs-tsi.h>
35 #include <plat/gpio-cfg.h>
37 #if defined(CONFIG_CPU_S5PV210) || defined(CONFIG_TARGET_LOCALE_NTT)
38 #include <linux/sched.h>
39 #include <linux/wait.h>
40 #include <linux/poll.h>
41 #include <linux/slab.h>
44 #if defined(CONFIG_CPU_S5PV210) || defined(CONFIG_TARGET_LOCALE_NTT)
45 #define TSI_BUF_SIZE (128*1024)
46 #define TSI_PKT_CNT 16
48 #define TSI_BUF_SIZE (256*1024)
56 enum pid_filter_mode {
61 enum data_byte_order {
66 struct list_head list;
74 enum filter_mode flt_mode;
75 enum pid_filter_mode pid_flt_mode;
76 enum data_byte_order byte_order;
88 struct resource *tsi_mem;
89 /* struct resource *tsi_irq; */
90 void __iomem *tsi_base;
93 #if defined(CONFIG_PM) && defined(CONFIG_TARGET_LOCALE_NTT)
94 int last_running_state;
97 dma_addr_t tsi_buf_phy;
100 s3c_tsi_conf *tsi_conf;
101 struct list_head free_list;
102 struct list_head full_list;
103 struct list_head partial_list;
104 wait_queue_head_t read_wq;
109 static struct platform_device *s3c_tsi_dev;
111 /* #define DRIVER_LOGIC_CHK */
112 #ifdef DRIVER_LOGIC_CHK
113 static struct timer_list tsi_timer;
118 #define TSI_DEBUG(fmt, ...) \
121 "%s: " fmt, __func__, ##__VA_ARGS__); \
124 #define TSI_WARN(fmt, ...) \
126 printk(KERN_WARNING \
127 fmt, ##__VA_ARGS__); \
130 #define TSI_ERROR(fmt, ...) \
133 "%s: " fmt, __func__, ##__VA_ARGS__); \
137 /*#define CONFIG_VIDEO_TSI_DEBUG */
138 #ifdef CONFIG_VIDEO_TSI_DEBUG
139 #define tsi_dbg(fmt, ...) TSI_DEBUG(fmt, ##__VA_ARGS__)
141 #define tsi_dbg(fmt, ...)
144 #define tsi_warn(fmt, ...) TSI_WARN(fmt, ##__VA_ARGS__)
145 #define tsi_err(fmt, ...) TSI_ERROR(fmt, ##__VA_ARGS__)
147 #define tsi_list_dbg(fmt, ...) TSI_DEBUG(fmt, ##__VA_ARGS__)
150 #ifdef CONFIG_TSI_LIST_DEBUG
151 void list_debug(struct list_head *head)
155 /* tsi_list_dbg("DEBUGGING FREE LIST\n"); */
157 list_for_each_entry(pkt, head, list) {
158 tsi_list_dbg(" node %d node_addr %x physical add %p virt add %p size %d\n",
159 i, pkt, pkt->addr, pkt->buf, pkt->len);
165 /*This should be done in platform*/
166 void s3c_tsi_set_gpio(void)
169 s3c_gpio_cfgpin(EXYNOS4210_GPE0(0), S3C_GPIO_SFN(4));
170 s3c_gpio_setpull(EXYNOS4210_GPE0(0), S3C_GPIO_PULL_NONE);
173 s3c_gpio_cfgpin(EXYNOS4210_GPE0(2), S3C_GPIO_SFN(4));
174 s3c_gpio_setpull(EXYNOS4210_GPE0(2), S3C_GPIO_PULL_NONE);
176 #if defined(CONFIG_TARGET_LOCALE_NTT)
177 printk(" %s : system_rev %d\n", __func__, system_rev);
179 if (system_rev >= 11) {
181 s3c_gpio_cfgpin(EXYNOS4210_GPE0(3), S3C_GPIO_SFN(4));
182 s3c_gpio_setpull(EXYNOS4210_GPE0(3), S3C_GPIO_PULL_NONE);
186 s3c_gpio_cfgpin(S5PV310_GPE0(3), S3C_GPIO_SFN(4));
187 s3c_gpio_setpull(S5PV310_GPE0(3), S3C_GPIO_PULL_NONE);
190 #if !defined(CONFIG_TARGET_LOCALE_NTT)
192 s3c_gpio_cfgpin(S5PV310_GPE0(1), S3C_GPIO_SFN(4));
193 s3c_gpio_setpull(S5PV310_GPE0(1), S3C_GPIO_PULL_NONE);
198 void s3c_tsi_reset(tsi_dev *tsi)
201 tscon = readl((tsi->tsi_base + S3C_TS_CON));
202 tscon |= S3C_TSI_SWRESET ;
203 writel(tscon, (tsi->tsi_base + S3C_TS_CON));
206 void s3c_tsi_set_timeout(u32 count, tsi_dev *tsi)
208 writel(count, (tsi->tsi_base + S3C_TS_CNT));
211 tsi_pkt *tsi_get_pkt(tsi_dev *tsi, struct list_head *head)
215 spin_lock_irqsave(&tsi->tsi_lock, flags);
217 if (list_empty(head)) {
218 tsi_err("TSI %p list is null\n", head);
219 spin_unlock_irqrestore(&tsi->tsi_lock, flags);
222 pkt = list_first_entry(head, tsi_pkt, list);
223 spin_unlock_irqrestore(&tsi->tsi_lock, flags);
228 void s3c_tsi_set_dest_addr(dma_addr_t addr, u32 reg)
233 void s3c_tsi_set_sync_mode(u8 mode, u32 reg)
238 void s3c_tsi_set_clock(u8 enable, u32 reg)
246 void tsi_enable_interrupts(tsi_dev *tsi)
249 /* Enable all the interrupts... */
251 writel(mask, (tsi->tsi_base + S3C_TS_INTMASK));
254 void tsi_disable_interrupts(tsi_dev *tsi)
256 writel(0, (tsi->tsi_base + S3C_TS_INTMASK));
259 static int s3c_tsi_start(tsi_dev *tsi)
264 pkt1 = tsi_get_pkt(tsi, &tsi->free_list);
266 tsi_err("Failed to start TSI--No buffers avaialble\n");
269 pkt_size = pkt1->len;
270 #if defined(CONFIG_CPU_S5PV210) || defined(CONFIG_TARGET_LOCALE_NTT)
271 /* when set the TS BUF SIZE to the S3C_TS_SIZE,
272 if you want get a 10-block TS from TSIF,
273 you should set the value of S3C_TS_SIZE as 47*10(not 188*10)
274 This register get a value of word-multiple values.
275 So, pkt_size which is counted to BYTES must be divided by 4
277 Commented by sjinu, 2009_03_18
279 writel(pkt_size>>2, (tsi->tsi_base+S3C_TS_SIZE));
281 writel(pkt_size, (tsi->tsi_base+S3C_TS_SIZE));
283 s3c_tsi_set_dest_addr(pkt1->addr, (u32)(tsi->tsi_base+S3C_TS_BASE));
285 spin_lock_irqsave(&tsi->tsi_lock, flags);
286 list_move_tail(&pkt1->list, &tsi->partial_list);
287 spin_unlock_irqrestore(&tsi->tsi_lock, flags);
288 /* start the clock */
289 s3c_tsi_set_clock(TSI_CLK_START, (u32)(tsi->tsi_base+S3C_TS_CLKCON));
290 /* set the next buffer immediatly */
291 pkt1 = tsi_get_pkt(tsi, &tsi->free_list);
293 tsi_err("Failed to start TSI--No buffers avaialble\n");
296 s3c_tsi_set_dest_addr(pkt1->addr, (u32)(tsi->tsi_base+S3C_TS_BASE));
297 spin_lock_irqsave(&tsi->tsi_lock, flags);
298 list_move_tail(&pkt1->list, &tsi->partial_list);
299 spin_unlock_irqrestore(&tsi->tsi_lock, flags);
300 tsi_enable_interrupts(tsi);
302 #ifdef CONFIG_TSI_LIST_DEBUG1
303 tsi_list_dbg("Debugging Partial list\n");
304 list_debug(&tsi->partial_list);
305 tsi_list_dbg("Debugging free list\n");
306 list_debug(&tsi->free_list);
311 static int s3c_tsi_stop(tsi_dev *tsi)
315 struct list_head *full = &tsi->full_list;
316 struct list_head *partial = &tsi->partial_list;
318 spin_lock_irqsave(&tsi->tsi_lock, flags);
319 #ifdef DRIVER_LOGIC_CHK
320 del_timer(&tsi_timer);
323 tsi_disable_interrupts(tsi);
324 s3c_tsi_set_clock(TSI_CLK_STOP, (u32)(tsi->tsi_base+S3C_TS_CLKCON));
325 /* move all the packets from partial and full list to free list */
326 while (!list_empty(full)) {
327 pkt = list_entry(full->next, tsi_pkt, list);
328 list_move_tail(&pkt->list, &tsi->free_list);
331 while (!list_empty(partial)) {
332 pkt = list_entry(partial->next, tsi_pkt, list);
333 list_move_tail(&pkt->list, &tsi->free_list);
336 tsi_priv->new_pkt = 0;
337 spin_unlock_irqrestore(&tsi->tsi_lock, flags);
342 void s3c_tsi_setup(tsi_dev *tsi)
345 s3c_tsi_conf *conf = tsi->tsi_conf;
347 s3c_tsi_set_timeout(TS_TIMEOUT_CNT_MAX, tsi);
349 tscon = readl((tsi->tsi_base+S3C_TS_CON));
351 tscon &= ~(S3C_TSI_SWRESET_MASK|S3C_TSI_CLKFILTER_MASK|
352 S3C_TSI_BURST_LEN_MASK | S3C_TSI_INT_FIFO_FULL_INT_ENA_MASK |
353 S3C_TSI_SYNC_MISMATCH_INT_MASK | S3C_TSI_PSUF_INT_MASK|
354 S3C_TSI_PSOF_INT_MASK | S3C_TSI_TS_CLK_TIME_OUT_INT_MASK |
355 S3C_TSI_TS_ERROR_MASK | S3C_TSI_PID_FILTER_MASK |
356 S3C_TSI_ERROR_ACTIVE_MASK | S3C_TSI_DATA_BYTE_ORDER_MASK |
357 S3C_TSI_TS_VALID_ACTIVE_MASK | S3C_TSI_SYNC_ACTIVE_MASK |
358 S3C_TSI_CLK_INVERT_MASK);
360 tscon |= (conf->flt_mode << S3C_TSI_CLKFILTER_SHIFT);
361 tscon |= (conf->pid_flt_mode << S3C_TSI_PID_FILTER_SHIFT);
362 tscon |= (conf->byte_order << S3C_TSI_DATA_BYTE_ORDER_SHIFT);
363 tscon |= (conf->burst_len << S3C_TSI_BURST_LEN_SHIFT);
364 tscon |= (conf->pad_pattern << S3C_TSI_PAD_PATTERN_SHIFT);
366 tscon |= (S3C_TSI_OUT_BUF_FULL_INT_ENA | S3C_TSI_INT_FIFO_FULL_INT_ENA);
367 tscon |= (S3C_TSI_SYNC_MISMATCH_INT_SKIP | S3C_TSI_PSUF_INT_SKIP |
368 S3C_TSI_PSOF_INT_SKIP);
369 tscon |= (S3C_TSI_TS_CLK_TIME_OUT_INT);
370 /* These values are bd dependent? */
371 tscon |= (S3C_TSI_TS_VALID_ACTIVE_HIGH | S3C_TSI_CLK_INVERT_HIGH);
372 writel(tscon, (tsi->tsi_base+S3C_TS_CON));
373 s3c_tsi_set_sync_mode(conf->sync_detect, (u32)(tsi->tsi_base+S3C_TS_SYNC));
376 void s3c_tsi_rx_int(tsi_dev *tsi)
379 /* deque the pcket from partial list to full list
380 incase the free list is empty, stop the tsi.. */
382 pkt = tsi_get_pkt(tsi, &tsi->partial_list);
384 /* this situation should not come.. stop_tsi */
386 tsi_err("TSI..Receive interrupt without buffer\n");
391 tsi_dbg("moving %p node %x phy %p virt to full list\n",
392 pkt, pkt->addr, pkt->buf);
394 list_move_tail(&pkt->list, &tsi->full_list);
396 pkt = tsi_get_pkt(tsi, &tsi->free_list);
398 /* this situation should not come.. stop_tsi */
399 tsi_err("TSI..No more free bufs..stopping channel\n");
403 list_move_tail(&pkt->list, &tsi->partial_list);
405 #if defined(CONFIG_CPU_S5PV210) || defined(CONFIG_TARGET_LOCALE_NTT)
406 /* namkh, request from Abraham
407 If there arise a buffer-full interrupt,
408 a new ts buffer address should be set.
410 Commented by sjinu, 2009_03_18 */
411 s3c_tsi_set_dest_addr(pkt->addr, (u32)(tsi->tsi_base+S3C_TS_BASE));
414 #ifdef CONFIG_TSI_LIST_DEBUG
415 tsi_list_dbg("Debugging Full list\n");
416 list_debug(&tsi->full_list);
417 tsi_list_dbg("Debugging Partial list\n");
418 list_debug(&tsi->partial_list);
421 wake_up(&tsi->read_wq);
425 static irqreturn_t s3c_tsi_irq(int irq, void *dev_id)
428 tsi_dev *tsi = platform_get_drvdata((struct platform_device *)dev_id);
429 intpnd = readl(tsi->tsi_base + S3C_TS_INT);
430 tsi_dbg("INTPND is %x\n", intpnd);
431 writel(intpnd, (tsi->tsi_base+S3C_TS_INT));
433 if (intpnd & S3C_TSI_OUT_BUF_FULL)
438 static int s3c_tsi_release(struct inode *inode, struct file *file)
441 tsi_dev *tsi = file->private_data;
442 tsi_dbg("TSI_RELEASE\n");
444 tsi_dbg("TSI_RELEASE stopping\n");
446 ret = s3c_tsi_stop(tsi);
447 tsi_dbg("TSI_RELEASE LIST cleaned\n");
450 #ifdef CONFIG_TSI_LIST_DEBUG
451 tsi_list_dbg("Debugging Full list\n");
452 list_debug(&tsi->full_list);
453 tsi_list_dbg("Debugging Partial list\n");
454 list_debug(&tsi->partial_list);
460 int s3c_tsi_mmap(struct file *filp, struct vm_area_struct *vma)
465 #if defined(CONFIG_CPU_S5PV210) || defined(CONFIG_TARGET_LOCALE_NTT)
466 static unsigned int s3c_tsi_poll(struct file *file, poll_table *wait)
468 unsigned int mask = 0;
469 tsi_dev *tsi = file->private_data;
471 poll_wait(file, &tsi->read_wq, wait);
474 mask |= (POLLIN | POLLRDNORM);
480 static ssize_t s3c_tsi_read(struct file *file, char *buf, size_t count, loff_t *pos)
484 u32 len = 0, pkt_size = 0;
486 tsi_dev *tsi = file->private_data;
487 struct list_head *full = &tsi->full_list;
489 #ifdef CONFIG_TSI_LIST_DEBUG
490 tsi_list_dbg("Debugging Full list\n");
491 tsi_dbg("count is %d\n", count);
492 list_debug(&tsi->full_list);
495 #if defined(CONFIG_CPU_S5PV210) || defined(CONFIG_TARGET_LOCALE_NTT)
496 ret = wait_event_interruptible(tsi->read_wq, tsi->new_pkt);
498 tsi_dbg("woken up from signal..returning\n");
501 pkt = tsi_get_pkt(tsi, full);
503 pkt_size = pkt->len; /* pkt_size should be multiple of 188 bytes. */
505 tsi_dbg("pkt_size is %d\n", pkt_size);
506 if (pkt_size > count)
509 if (copy_to_user((buf+len), pkt->buf, pkt_size)) {
510 tsi_dbg("copy user fail\n");
517 tsi_dbg("len is%d count %d pkt_size %d\n", len, count, pkt_size);
519 spin_lock_irqsave(&tsi->tsi_lock, flags);
520 list_move(&pkt->list, &tsi->free_list);
521 spin_unlock_irqrestore(&tsi->tsi_lock, flags);
523 if (list_empty(full))
527 /* deque packet from full list */
528 pkt = tsi_get_pkt(tsi, full);
530 ret = wait_event_interruptible(tsi->read_wq, tsi->new_pkt);
533 tsi_dbg("woken up from signal..returning\n");
536 tsi_dbg("woken up proprt\n");
537 pkt = tsi_get_pkt(tsi, full);
540 pkt_size = pkt->len * 4;
541 if (pkt_size > count)
544 if (copy_to_user((buf+len), pkt->buf, pkt_size)) {
545 tsi_dbg("copy user fail\n");
552 tsi_dbg("len is%d count %d pkt_size %d\n", len, count, pkt_size);
554 spin_lock_irqsave(&tsi->tsi_lock, flags);
555 list_move(&pkt->list, &tsi->free_list);
556 spin_unlock_irqrestore(&tsi->tsi_lock, flags);
558 if (list_empty(full))
563 #ifdef CONFIG_TSI_LIST_DEBUG1
564 tsi_list_dbg("Debugging Free list\n");
565 list_debug(&tsi->free_list);
570 #define TSI_TRIGGER 0xAABB
571 #define TSI_STOP 0xAACC
573 static long s3c_tsi_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
576 tsi_dev *tsi = platform_get_drvdata(s3c_tsi_dev);
577 /* currently only two ioctl for tigger and stop are provided.. */
578 tsi_dbg("TSI cmd is %x\n", cmd);
584 ret = s3c_tsi_start(tsi);
585 #ifdef DRIVER_LOGIC_CHK
586 tsi_timer.expires = jiffies + HZ/10;
587 add_timer(&tsi_timer);
592 ret = s3c_tsi_stop(tsi);
600 static int s3c_tsi_open(struct inode *inode, struct file *file)
602 tsi_dev *s3c_tsi = platform_get_drvdata(s3c_tsi_dev);
603 tsi_dbg(" %s\n", __func__);
604 #if defined(CONFIG_CPU_S5PV210) || defined(CONFIG_TARGET_LOCALE_NTT)
605 /* Fix the TSI data problem (Don't generated waking up sleep state)
606 clk_enable(s3c_tsi->tsi_clk);
608 s3c_tsi_setup(s3c_tsi);
610 file->private_data = s3c_tsi;
614 static struct file_operations tsi_fops = {
617 release : s3c_tsi_release,
618 unlocked_ioctl : s3c_tsi_ioctl,
620 #if defined(CONFIG_CPU_S5PV210) || defined(CONFIG_TARGET_LOCALE_NTT)
627 static struct miscdevice s3c_tsi_miscdev = {
628 minor: MISC_DYNAMIC_MINOR,
633 static int tsi_setup_bufs(tsi_dev *dev, struct list_head *head)
636 u32 tsi_virt, tsi_size, buf_size;
641 tsi_phy = dev->tsi_buf_phy;
642 tsi_virt = (u32) dev->tsi_buf_virt;
643 tsi_size = dev->tsi_buf_size;
644 #if defined(CONFIG_CPU_S5PV210) || defined(CONFIG_TARGET_LOCALE_NTT)
645 /* TSI generates interrupt after filling this many bytes */
646 buf_size = dev->tsi_conf->num_packet * TS_PKT_SIZE*TSI_PKT_CNT;
648 /* TSI generates interrupt after filling this many bytes */
649 buf_size = dev->tsi_conf->num_packet * TS_PKT_SIZE;
651 num_buf = (tsi_size / buf_size);
653 for (i = 0; i < num_buf; i++) {
654 pkt = kmalloc(sizeof(tsi_pkt), GFP_KERNEL);
656 return list_empty(head) ? -ENOMEM : 0 ;
657 #if defined(CONFIG_CPU_S5PV210) || defined(CONFIG_TARGET_LOCALE_NTT)
658 /* Address should be byte-aligned
659 Commented by sjinu, 2009_03_18 */
660 pkt->addr = ((u32)tsi_phy + i*buf_size);
661 pkt->buf = (void *)(u8 *)((u32)tsi_virt + i*buf_size);
663 pkt->addr = (tsi_phy + i*4*buf_size);
664 pkt->buf = (void *)(tsi_virt + i*4*buf_size);
667 list_add_tail(&pkt->list, head);
670 tsi_dbg("total nodes calulated %d buf_size %d\n", num_buf, buf_size);
671 #ifdef CONFIG_TSI_LIST_DEBUG1
679 #ifdef DRIVER_LOGIC_CHK
680 int timer_count = 100;
682 void tsi_timer_function(u32 dev)
684 tsi_dev *tsi = (tsi_dev *)(dev);
686 tsi_timer.expires = jiffies + HZ/100;
689 add_timer(&tsi_timer);
693 static int s3c_tsi_probe(struct platform_device *pdev)
695 struct resource *res;
700 struct device *dev = &pdev->dev;
702 tsi_dbg(" %s\n", __func__);
703 tsi_priv = kmalloc(sizeof(tsi_dev), GFP_KERNEL);
704 if (tsi_priv == NULL) {
705 printk("NO Memory for tsi allocation\n");
708 conf = kmalloc(sizeof(s3c_tsi_conf), GFP_KERNEL);
710 printk("NO Memory for tsi conf allocation\n");
714 /* Initialise the dafault conf parameters..
715 * this should be obtained from the platform data and ioctl
716 * move this to platform later */
718 conf->flt_mode = OFF;
719 conf->pid_flt_mode = BYPASS;
720 conf->byte_order = MSB2LSB;
721 #if defined(CONFIG_TARGET_LOCALE_NTT)
722 conf->sync_detect = S3C_TSI_SYNC_DET_MODE_TS_SYNC_BYTE;
724 conf->sync_detect = S3C_TSI_SYNC_DET_MODE_TS_SYNC8;
727 #if defined(CONFIG_CPU_S5PV210) || defined(CONFIG_TARGET_LOCALE_NTT)
729 to avoid making interrupt during getting the TS from TS buffer,
730 we use the burst-length as 8 beat.
731 This burst-length may be changed next time.
732 Commented by sjinu, 2009_03_18
738 conf->byte_swap = 1; /* little endian */
739 conf->pad_pattern = 0; /* this might vary from bd to bd */
740 conf->num_packet = TS_NUM_PKT; /* this might vary from bd to bd */
742 tsi_priv->tsi_conf = conf;
743 tsi_priv->tsi_buf_size = TSI_BUF_SIZE;
745 tsi_priv->tsi_clk = clk_get(NULL, "tsi");
746 //printk("Clk Get Result %x\n", tsi_priv->tsi_clk);
747 if (tsi_priv->tsi_clk == NULL) {
748 printk(KERN_ERR "Failed to get TSI clock\n");
751 clk_enable(tsi_priv->tsi_clk);
753 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
756 tsi_err("failed to get memory region resouce\n");
760 size = (res->end - res->start) + 1;
761 tsi_priv->tsi_mem = request_mem_region(res->start, size, pdev->name);
763 if (tsi_priv->tsi_mem == NULL) {
764 tsi_err("failed to get memory region\n");
768 ret = platform_get_irq(pdev, 0);
771 tsi_err("failed to get irq resource\n");
776 tsi_priv->tsi_irq = ret;
777 ret = request_irq(tsi_priv->tsi_irq, (void *)s3c_tsi_irq, 0, pdev->name, pdev);
780 tsi_err("failed to install irq (%d)\n", ret);
784 tsi_priv->tsi_base = ioremap(tsi_priv->tsi_mem->start, size);
786 if (tsi_priv->tsi_base == 0) {
787 tsi_err("failed to ioremap() region\n");
792 INIT_LIST_HEAD(&tsi_priv->free_list);
793 INIT_LIST_HEAD(&tsi_priv->full_list);
794 INIT_LIST_HEAD(&tsi_priv->partial_list);
795 spin_lock_init(&tsi_priv->tsi_lock);
796 init_waitqueue_head(&tsi_priv->read_wq);
797 tsi_priv->new_pkt = 0;
798 tsi_priv->running = 0;
799 #if defined(CONFIG_PM) && defined(CONFIG_TARGET_LOCALE_NTT)
800 tsi_priv->last_running_state = tsi_priv->running;
803 /* get the dma coherent mem */
804 tsi_priv->tsi_buf_virt = dma_alloc_coherent(dev, tsi_priv->tsi_buf_size, &map_dma, GFP_KERNEL);
805 if (tsi_priv->tsi_buf_virt == NULL) {
806 tsi_err("Failed to claim TSI memory\n");
811 tsi_dbg("TSI dev dma mem phy %x virt %p\n", map_dma, tsi_priv->tsi_buf_virt);
813 tsi_priv->tsi_buf_phy = map_dma;
815 ret = tsi_setup_bufs(tsi_priv, &tsi_priv->free_list);
817 tsi_err("TSI failed to setup pkt list");
821 platform_set_drvdata(pdev, tsi_priv);
823 s3c_tsi_setup(tsi_priv);
825 ret = misc_register(&s3c_tsi_miscdev);
827 tsi_err("Unable to register the s3c-tsi driver\n");
831 #ifdef DRIVER_LOGIC_CHK
832 init_timer(&tsi_timer);
833 tsi_timer.function = tsi_timer_function;
834 tsi_timer.data = (unsigned long) tsi_priv;
836 s3c_tsi_start(tsi_priv);
837 s3c_tsi_rx_int(tsi_priv);
844 clk_disable(tsi_priv->tsi_clk);
846 iounmap(tsi_priv->tsi_base);
848 free_irq(tsi_priv->tsi_irq, pdev);
850 release_resource(tsi_priv->tsi_mem);
856 static void tsi_free_packets(tsi_dev *tsi)
859 struct list_head *head = &(tsi->free_list);
861 while (!list_empty(head)) {
862 pkt = list_entry(head->next, tsi_pkt, list);
863 list_del(&pkt->list);
868 static int s3c_tsi_remove(struct platform_device *dev)
870 tsi_dev *tsi = platform_get_drvdata((struct platform_device *)dev);
874 /* free allocated memory and nodes */
875 tsi_free_packets(tsi);
876 free_irq(tsi->tsi_irq, dev);
877 dma_free_coherent(&dev->dev, tsi->tsi_buf_size, tsi->tsi_buf_virt, tsi->tsi_buf_phy);
883 #if defined(CONFIG_PM) && defined(CONFIG_TARGET_LOCALE_NTT)
884 static int s3c_tsi_suspend(struct platform_device *pdev, pm_message_t state)
886 tsi_dev *tsi = platform_get_drvdata(s3c_tsi_dev);
888 tsi->last_running_state = tsi->running;
889 if (tsi_priv->last_running_state)
890 s3c_tsi_stop(tsi_priv);
892 clk_disable(tsi_priv->tsi_clk);
897 static int s3c_tsi_resume(struct platform_device *pdev)
899 tsi_dev *tsi = platform_get_drvdata(s3c_tsi_dev);
901 clk_enable(tsi_priv->tsi_clk);
903 s3c_tsi_setup(tsi_priv);
905 if (tsi->last_running_state) {
915 static struct platform_driver s3c_tsi_driver = {
916 .probe = s3c_tsi_probe,
917 .remove = s3c_tsi_remove,
919 #if defined(CONFIG_PM) && defined(CONFIG_TARGET_LOCALE_NTT)
920 .suspend = s3c_tsi_suspend,
921 .resume = s3c_tsi_resume,
927 .owner = THIS_MODULE,
934 const char banner[] __initdata = "TSI Driver Version 1.0\n";
936 static int __init s3c_tsi_init(void)
939 tsi_dbg(" %s\n", __func__);
940 return platform_driver_register(&s3c_tsi_driver);
946 static void __exit s3c_tsi_exit(void)
949 platform_driver_unregister(&s3c_tsi_driver);
954 module_init(s3c_tsi_init);
955 module_exit(s3c_tsi_exit);
957 MODULE_AUTHOR("Samsung");
958 MODULE_DESCRIPTION("S3C TSI Device Driver");
959 MODULE_LICENSE("GPL");