diff options
author | Nibble Max <nibble.max@gmail.com> | 2015-06-29 10:09:42 -0400 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@osg.samsung.com> | 2015-07-06 07:26:16 -0400 |
commit | 8783b9c50400c6279d7c3b716637b98e83d3c933 (patch) | |
tree | a09d2893a01a679ced14f242d9d1fd8b4ebc67f2 | |
parent | f459aec2bc81a46b674901424295f8ffe5e29ad0 (diff) |
[media] SMI PCIe IR driver for DVBSky cards
Ported from the manufacturer's source tree, available from
http://dvbsky.net/download/linux/media_build-bst-150211.tar.gz
This is the second patch after a public review.
[mchehab@osg.samsung.com: fix inconsistent identing warning]
Signed-off-by: Dirk Nehring <dnehring@gmx.net>
Reviewd-by: Nibble Max <nibble.max@gmail.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
-rw-r--r-- | drivers/media/pci/smipcie/Kconfig | 1 | ||||
-rw-r--r-- | drivers/media/pci/smipcie/Makefile | 3 | ||||
-rw-r--r-- | drivers/media/pci/smipcie/smipcie-ir.c | 232 | ||||
-rw-r--r-- | drivers/media/pci/smipcie/smipcie-main.c (renamed from drivers/media/pci/smipcie/smipcie.c) | 14 | ||||
-rw-r--r-- | drivers/media/pci/smipcie/smipcie.h | 19 |
5 files changed, 268 insertions, 1 deletions
diff --git a/drivers/media/pci/smipcie/Kconfig b/drivers/media/pci/smipcie/Kconfig index 21a1583dbd8f..c11c772830c9 100644 --- a/drivers/media/pci/smipcie/Kconfig +++ b/drivers/media/pci/smipcie/Kconfig | |||
@@ -7,6 +7,7 @@ config DVB_SMIPCIE | |||
7 | select DVB_TS2020 if MEDIA_SUBDRV_AUTOSELECT | 7 | select DVB_TS2020 if MEDIA_SUBDRV_AUTOSELECT |
8 | select MEDIA_TUNER_M88RS6000T if MEDIA_SUBDRV_AUTOSELECT | 8 | select MEDIA_TUNER_M88RS6000T if MEDIA_SUBDRV_AUTOSELECT |
9 | select MEDIA_TUNER_SI2157 if MEDIA_SUBDRV_AUTOSELECT | 9 | select MEDIA_TUNER_SI2157 if MEDIA_SUBDRV_AUTOSELECT |
10 | depends on RC_CORE | ||
10 | help | 11 | help |
11 | Support for cards with SMI PCIe bridge: | 12 | Support for cards with SMI PCIe bridge: |
12 | - DVBSky S950 V3 | 13 | - DVBSky S950 V3 |
diff --git a/drivers/media/pci/smipcie/Makefile b/drivers/media/pci/smipcie/Makefile index be55481a6e95..013bc3fe4294 100644 --- a/drivers/media/pci/smipcie/Makefile +++ b/drivers/media/pci/smipcie/Makefile | |||
@@ -1,3 +1,6 @@ | |||
1 | |||
2 | smipcie-objs := smipcie-main.o smipcie-ir.o | ||
3 | |||
1 | obj-$(CONFIG_DVB_SMIPCIE) += smipcie.o | 4 | obj-$(CONFIG_DVB_SMIPCIE) += smipcie.o |
2 | 5 | ||
3 | ccflags-y += -Idrivers/media/tuners | 6 | ccflags-y += -Idrivers/media/tuners |
diff --git a/drivers/media/pci/smipcie/smipcie-ir.c b/drivers/media/pci/smipcie/smipcie-ir.c new file mode 100644 index 000000000000..d018673c71f6 --- /dev/null +++ b/drivers/media/pci/smipcie/smipcie-ir.c | |||
@@ -0,0 +1,232 @@ | |||
1 | /* | ||
2 | * SMI PCIe driver for DVBSky cards. | ||
3 | * | ||
4 | * Copyright (C) 2014 Max nibble <nibble.max@gmail.com> | ||
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 | |||
17 | #include "smipcie.h" | ||
18 | |||
19 | static void smi_ir_enableInterrupt(struct smi_rc *ir) | ||
20 | { | ||
21 | struct smi_dev *dev = ir->dev; | ||
22 | |||
23 | smi_write(MSI_INT_ENA_SET, IR_X_INT); | ||
24 | } | ||
25 | |||
26 | static void smi_ir_disableInterrupt(struct smi_rc *ir) | ||
27 | { | ||
28 | struct smi_dev *dev = ir->dev; | ||
29 | |||
30 | smi_write(MSI_INT_ENA_CLR, IR_X_INT); | ||
31 | } | ||
32 | |||
33 | static void smi_ir_clearInterrupt(struct smi_rc *ir) | ||
34 | { | ||
35 | struct smi_dev *dev = ir->dev; | ||
36 | |||
37 | smi_write(MSI_INT_STATUS_CLR, IR_X_INT); | ||
38 | } | ||
39 | |||
40 | static void smi_ir_stop(struct smi_rc *ir) | ||
41 | { | ||
42 | struct smi_dev *dev = ir->dev; | ||
43 | |||
44 | smi_ir_disableInterrupt(ir); | ||
45 | smi_clear(IR_Init_Reg, 0x80); | ||
46 | } | ||
47 | |||
48 | #define BITS_PER_COMMAND 14 | ||
49 | #define GROUPS_PER_BIT 2 | ||
50 | #define IR_RC5_MIN_BIT 36 | ||
51 | #define IR_RC5_MAX_BIT 52 | ||
52 | static u32 smi_decode_rc5(u8 *pData, u8 size) | ||
53 | { | ||
54 | u8 index, current_bit, bit_count; | ||
55 | u8 group_array[BITS_PER_COMMAND * GROUPS_PER_BIT + 4]; | ||
56 | u8 group_index = 0; | ||
57 | u32 command = 0xFFFFFFFF; | ||
58 | |||
59 | group_array[group_index++] = 1; | ||
60 | |||
61 | for (index = 0; index < size; index++) { | ||
62 | |||
63 | current_bit = (pData[index] & 0x80) ? 1 : 0; | ||
64 | bit_count = pData[index] & 0x7f; | ||
65 | |||
66 | if ((current_bit == 1) && (bit_count >= 2*IR_RC5_MAX_BIT + 1)) { | ||
67 | goto process_code; | ||
68 | } else if ((bit_count >= IR_RC5_MIN_BIT) && | ||
69 | (bit_count <= IR_RC5_MAX_BIT)) { | ||
70 | group_array[group_index++] = current_bit; | ||
71 | } else if ((bit_count > IR_RC5_MAX_BIT) && | ||
72 | (bit_count <= 2*IR_RC5_MAX_BIT)) { | ||
73 | group_array[group_index++] = current_bit; | ||
74 | group_array[group_index++] = current_bit; | ||
75 | } else { | ||
76 | goto invalid_timing; | ||
77 | } | ||
78 | if (group_index >= BITS_PER_COMMAND*GROUPS_PER_BIT) | ||
79 | goto process_code; | ||
80 | |||
81 | if ((group_index == BITS_PER_COMMAND*GROUPS_PER_BIT - 1) | ||
82 | && (group_array[group_index-1] == 0)) { | ||
83 | group_array[group_index++] = 1; | ||
84 | goto process_code; | ||
85 | } | ||
86 | } | ||
87 | |||
88 | process_code: | ||
89 | if (group_index == (BITS_PER_COMMAND*GROUPS_PER_BIT-1)) | ||
90 | group_array[group_index++] = 1; | ||
91 | |||
92 | if (group_index == BITS_PER_COMMAND*GROUPS_PER_BIT) { | ||
93 | command = 0; | ||
94 | for (index = 0; index < (BITS_PER_COMMAND*GROUPS_PER_BIT); | ||
95 | index = index + 2) { | ||
96 | if ((group_array[index] == 1) && | ||
97 | (group_array[index+1] == 0)) { | ||
98 | command |= (1 << (BITS_PER_COMMAND - | ||
99 | (index/2) - 1)); | ||
100 | } else if ((group_array[index] == 0) && | ||
101 | (group_array[index+1] == 1)) { | ||
102 | /* */ | ||
103 | } else { | ||
104 | command = 0xFFFFFFFF; | ||
105 | goto invalid_timing; | ||
106 | } | ||
107 | } | ||
108 | } | ||
109 | |||
110 | invalid_timing: | ||
111 | return command; | ||
112 | } | ||
113 | |||
114 | static void smi_ir_decode(struct work_struct *work) | ||
115 | { | ||
116 | struct smi_rc *ir = container_of(work, struct smi_rc, work); | ||
117 | struct smi_dev *dev = ir->dev; | ||
118 | struct rc_dev *rc_dev = ir->rc_dev; | ||
119 | u32 dwIRControl, dwIRData, dwIRCode, scancode; | ||
120 | u8 index, ucIRCount, readLoop, rc5_command, rc5_system, toggle; | ||
121 | |||
122 | dwIRControl = smi_read(IR_Init_Reg); | ||
123 | if (dwIRControl & rbIRVld) { | ||
124 | ucIRCount = (u8) smi_read(IR_Data_Cnt); | ||
125 | |||
126 | if (ucIRCount < 4) | ||
127 | goto end_ir_decode; | ||
128 | |||
129 | readLoop = ucIRCount/4; | ||
130 | if (ucIRCount % 4) | ||
131 | readLoop += 1; | ||
132 | for (index = 0; index < readLoop; index++) { | ||
133 | dwIRData = smi_read(IR_DATA_BUFFER_BASE + (index*4)); | ||
134 | |||
135 | ir->irData[index*4 + 0] = (u8)(dwIRData); | ||
136 | ir->irData[index*4 + 1] = (u8)(dwIRData >> 8); | ||
137 | ir->irData[index*4 + 2] = (u8)(dwIRData >> 16); | ||
138 | ir->irData[index*4 + 3] = (u8)(dwIRData >> 24); | ||
139 | } | ||
140 | dwIRCode = smi_decode_rc5(ir->irData, ucIRCount); | ||
141 | |||
142 | if (dwIRCode != 0xFFFFFFFF) { | ||
143 | rc5_command = dwIRCode & 0x3F; | ||
144 | rc5_system = (dwIRCode & 0x7C0) >> 6; | ||
145 | toggle = (dwIRCode & 0x800) ? 1 : 0; | ||
146 | scancode = rc5_system << 8 | rc5_command; | ||
147 | rc_keydown(rc_dev, RC_TYPE_RC5, scancode, toggle); | ||
148 | } | ||
149 | } | ||
150 | end_ir_decode: | ||
151 | smi_set(IR_Init_Reg, 0x04); | ||
152 | smi_ir_enableInterrupt(ir); | ||
153 | } | ||
154 | |||
155 | /* ir functions call by main driver.*/ | ||
156 | int smi_ir_irq(struct smi_rc *ir, u32 int_status) | ||
157 | { | ||
158 | int handled = 0; | ||
159 | |||
160 | if (int_status & IR_X_INT) { | ||
161 | smi_ir_disableInterrupt(ir); | ||
162 | smi_ir_clearInterrupt(ir); | ||
163 | schedule_work(&ir->work); | ||
164 | handled = 1; | ||
165 | } | ||
166 | return handled; | ||
167 | } | ||
168 | |||
169 | void smi_ir_start(struct smi_rc *ir) | ||
170 | { | ||
171 | struct smi_dev *dev = ir->dev; | ||
172 | |||
173 | smi_write(IR_Idle_Cnt_Low, 0x00140070); | ||
174 | msleep(20); | ||
175 | smi_set(IR_Init_Reg, 0x90); | ||
176 | |||
177 | smi_ir_enableInterrupt(ir); | ||
178 | } | ||
179 | |||
180 | int smi_ir_init(struct smi_dev *dev) | ||
181 | { | ||
182 | int ret; | ||
183 | struct rc_dev *rc_dev; | ||
184 | struct smi_rc *ir = &dev->ir; | ||
185 | |||
186 | rc_dev = rc_allocate_device(); | ||
187 | if (!rc_dev) | ||
188 | return -ENOMEM; | ||
189 | |||
190 | /* init input device */ | ||
191 | snprintf(ir->input_name, sizeof(ir->input_name), "IR (%s)", | ||
192 | dev->info->name); | ||
193 | snprintf(ir->input_phys, sizeof(ir->input_phys), "pci-%s/ir0", | ||
194 | pci_name(dev->pci_dev)); | ||
195 | |||
196 | rc_dev->driver_name = "SMI_PCIe"; | ||
197 | rc_dev->input_phys = ir->input_phys; | ||
198 | rc_dev->input_name = ir->input_name; | ||
199 | rc_dev->input_id.bustype = BUS_PCI; | ||
200 | rc_dev->input_id.version = 1; | ||
201 | rc_dev->input_id.vendor = dev->pci_dev->subsystem_vendor; | ||
202 | rc_dev->input_id.product = dev->pci_dev->subsystem_device; | ||
203 | rc_dev->dev.parent = &dev->pci_dev->dev; | ||
204 | |||
205 | rc_dev->driver_type = RC_DRIVER_SCANCODE; | ||
206 | rc_dev->map_name = RC_MAP_DVBSKY; | ||
207 | |||
208 | ir->rc_dev = rc_dev; | ||
209 | ir->dev = dev; | ||
210 | |||
211 | INIT_WORK(&ir->work, smi_ir_decode); | ||
212 | smi_ir_disableInterrupt(ir); | ||
213 | |||
214 | ret = rc_register_device(rc_dev); | ||
215 | if (ret) | ||
216 | goto ir_err; | ||
217 | |||
218 | return 0; | ||
219 | ir_err: | ||
220 | rc_free_device(rc_dev); | ||
221 | return ret; | ||
222 | } | ||
223 | |||
224 | void smi_ir_exit(struct smi_dev *dev) | ||
225 | { | ||
226 | struct smi_rc *ir = &dev->ir; | ||
227 | struct rc_dev *rc_dev = ir->rc_dev; | ||
228 | |||
229 | smi_ir_stop(ir); | ||
230 | rc_unregister_device(rc_dev); | ||
231 | ir->rc_dev = NULL; | ||
232 | } | ||
diff --git a/drivers/media/pci/smipcie/smipcie.c b/drivers/media/pci/smipcie/smipcie-main.c index 143fd7899ecd..b039a229b7d2 100644 --- a/drivers/media/pci/smipcie/smipcie.c +++ b/drivers/media/pci/smipcie/smipcie-main.c | |||
@@ -468,6 +468,7 @@ static irqreturn_t smi_irq_handler(int irq, void *dev_id) | |||
468 | struct smi_dev *dev = dev_id; | 468 | struct smi_dev *dev = dev_id; |
469 | struct smi_port *port0 = &dev->ts_port[0]; | 469 | struct smi_port *port0 = &dev->ts_port[0]; |
470 | struct smi_port *port1 = &dev->ts_port[1]; | 470 | struct smi_port *port1 = &dev->ts_port[1]; |
471 | struct smi_rc *ir = &dev->ir; | ||
471 | int handled = 0; | 472 | int handled = 0; |
472 | 473 | ||
473 | u32 intr_status = smi_read(MSI_INT_STATUS); | 474 | u32 intr_status = smi_read(MSI_INT_STATUS); |
@@ -480,6 +481,9 @@ static irqreturn_t smi_irq_handler(int irq, void *dev_id) | |||
480 | if (dev->info->ts_1) | 481 | if (dev->info->ts_1) |
481 | handled += smi_port_irq(port1, intr_status); | 482 | handled += smi_port_irq(port1, intr_status); |
482 | 483 | ||
484 | /* ir interrupt.*/ | ||
485 | handled += smi_ir_irq(ir, intr_status); | ||
486 | |||
483 | return IRQ_RETVAL(handled); | 487 | return IRQ_RETVAL(handled); |
484 | } | 488 | } |
485 | 489 | ||
@@ -993,6 +997,10 @@ static int smi_probe(struct pci_dev *pdev, const struct pci_device_id *id) | |||
993 | goto err_del_port0_attach; | 997 | goto err_del_port0_attach; |
994 | } | 998 | } |
995 | 999 | ||
1000 | ret = smi_ir_init(dev); | ||
1001 | if (ret < 0) | ||
1002 | goto err_del_port1_attach; | ||
1003 | |||
996 | #ifdef CONFIG_PCI_MSI /* to do msi interrupt.???*/ | 1004 | #ifdef CONFIG_PCI_MSI /* to do msi interrupt.???*/ |
997 | if (pci_msi_enabled()) | 1005 | if (pci_msi_enabled()) |
998 | ret = pci_enable_msi(dev->pci_dev); | 1006 | ret = pci_enable_msi(dev->pci_dev); |
@@ -1003,10 +1011,13 @@ static int smi_probe(struct pci_dev *pdev, const struct pci_device_id *id) | |||
1003 | ret = request_irq(dev->pci_dev->irq, smi_irq_handler, | 1011 | ret = request_irq(dev->pci_dev->irq, smi_irq_handler, |
1004 | IRQF_SHARED, "SMI_PCIE", dev); | 1012 | IRQF_SHARED, "SMI_PCIE", dev); |
1005 | if (ret < 0) | 1013 | if (ret < 0) |
1006 | goto err_del_port1_attach; | 1014 | goto err_del_ir; |
1007 | 1015 | ||
1016 | smi_ir_start(&dev->ir); | ||
1008 | return 0; | 1017 | return 0; |
1009 | 1018 | ||
1019 | err_del_ir: | ||
1020 | smi_ir_exit(dev); | ||
1010 | err_del_port1_attach: | 1021 | err_del_port1_attach: |
1011 | if (dev->info->ts_1) | 1022 | if (dev->info->ts_1) |
1012 | smi_port_detach(&dev->ts_port[1]); | 1023 | smi_port_detach(&dev->ts_port[1]); |
@@ -1039,6 +1050,7 @@ static void smi_remove(struct pci_dev *pdev) | |||
1039 | if (dev->info->ts_0) | 1050 | if (dev->info->ts_0) |
1040 | smi_port_detach(&dev->ts_port[0]); | 1051 | smi_port_detach(&dev->ts_port[0]); |
1041 | 1052 | ||
1053 | smi_ir_exit(dev); | ||
1042 | smi_i2c_exit(dev); | 1054 | smi_i2c_exit(dev); |
1043 | iounmap(dev->lmmio); | 1055 | iounmap(dev->lmmio); |
1044 | pci_set_drvdata(pdev, NULL); | 1056 | pci_set_drvdata(pdev, NULL); |
diff --git a/drivers/media/pci/smipcie/smipcie.h b/drivers/media/pci/smipcie/smipcie.h index 10cdf20f4839..68cdda28fd98 100644 --- a/drivers/media/pci/smipcie/smipcie.h +++ b/drivers/media/pci/smipcie/smipcie.h | |||
@@ -234,6 +234,17 @@ struct smi_cfg_info { | |||
234 | int fe_1; | 234 | int fe_1; |
235 | }; | 235 | }; |
236 | 236 | ||
237 | struct smi_rc { | ||
238 | struct smi_dev *dev; | ||
239 | struct rc_dev *rc_dev; | ||
240 | char input_phys[64]; | ||
241 | char input_name[64]; | ||
242 | struct work_struct work; | ||
243 | u8 irData[256]; | ||
244 | |||
245 | int users; | ||
246 | }; | ||
247 | |||
237 | struct smi_port { | 248 | struct smi_port { |
238 | struct smi_dev *dev; | 249 | struct smi_dev *dev; |
239 | int idx; | 250 | int idx; |
@@ -284,6 +295,9 @@ struct smi_dev { | |||
284 | /* i2c */ | 295 | /* i2c */ |
285 | struct i2c_adapter i2c_bus[2]; | 296 | struct i2c_adapter i2c_bus[2]; |
286 | struct i2c_algo_bit_data i2c_bit[2]; | 297 | struct i2c_algo_bit_data i2c_bit[2]; |
298 | |||
299 | /* ir */ | ||
300 | struct smi_rc ir; | ||
287 | }; | 301 | }; |
288 | 302 | ||
289 | #define smi_read(reg) readl(dev->lmmio + ((reg)>>2)) | 303 | #define smi_read(reg) readl(dev->lmmio + ((reg)>>2)) |
@@ -296,4 +310,9 @@ struct smi_dev { | |||
296 | #define smi_set(reg, bit) smi_andor((reg), (bit), (bit)) | 310 | #define smi_set(reg, bit) smi_andor((reg), (bit), (bit)) |
297 | #define smi_clear(reg, bit) smi_andor((reg), (bit), 0) | 311 | #define smi_clear(reg, bit) smi_andor((reg), (bit), 0) |
298 | 312 | ||
313 | int smi_ir_irq(struct smi_rc *ir, u32 int_status); | ||
314 | void smi_ir_start(struct smi_rc *ir); | ||
315 | void smi_ir_exit(struct smi_dev *dev); | ||
316 | int smi_ir_init(struct smi_dev *dev); | ||
317 | |||
299 | #endif /* #ifndef _SMI_PCIE_H_ */ | 318 | #endif /* #ifndef _SMI_PCIE_H_ */ |