diff options
Diffstat (limited to 'drivers/media/dvb/siano/smsir.c')
-rw-r--r-- | drivers/media/dvb/siano/smsir.c | 301 |
1 files changed, 301 insertions, 0 deletions
diff --git a/drivers/media/dvb/siano/smsir.c b/drivers/media/dvb/siano/smsir.c new file mode 100644 index 000000000000..a5f302c58fdb --- /dev/null +++ b/drivers/media/dvb/siano/smsir.c | |||
@@ -0,0 +1,301 @@ | |||
1 | /**************************************************************** | ||
2 | |||
3 | Siano Mobile Silicon, Inc. | ||
4 | MDTV receiver kernel modules. | ||
5 | Copyright (C) 2006-2009, Uri Shkolnik | ||
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, either version 2 of the License, or | ||
10 | (at your option) any later version. | ||
11 | |||
12 | This program is distributed in the hope that it will be useful, | ||
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | GNU General Public License for more details. | ||
16 | |||
17 | You should have received a copy of the GNU General Public License | ||
18 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
19 | |||
20 | ****************************************************************/ | ||
21 | |||
22 | |||
23 | #include <linux/types.h> | ||
24 | #include <linux/input.h> | ||
25 | |||
26 | #include "smscoreapi.h" | ||
27 | #include "smsir.h" | ||
28 | #include "sms-cards.h" | ||
29 | |||
30 | /* In order to add new IR remote control - | ||
31 | * 1) Add it to the <enum ir_kb_type> @ smsir,h, | ||
32 | * 2) Add its map to keyboard_layout_maps below | ||
33 | * 3) Set your board (sms-cards sub-module) to use it | ||
34 | */ | ||
35 | |||
36 | static struct keyboard_layout_map_t keyboard_layout_maps[] = { | ||
37 | [SMS_IR_KB_DEFAULT_TV] = { | ||
38 | .ir_protocol = IR_RC5, | ||
39 | .rc5_kbd_address = KEYBOARD_ADDRESS_TV1, | ||
40 | .keyboard_layout_map = { | ||
41 | KEY_0, KEY_1, KEY_2, | ||
42 | KEY_3, KEY_4, KEY_5, | ||
43 | KEY_6, KEY_7, KEY_8, | ||
44 | KEY_9, 0, 0, KEY_POWER, | ||
45 | KEY_MUTE, 0, 0, | ||
46 | KEY_VOLUMEUP, KEY_VOLUMEDOWN, | ||
47 | KEY_BRIGHTNESSUP, | ||
48 | KEY_BRIGHTNESSDOWN, KEY_CHANNELUP, | ||
49 | KEY_CHANNELDOWN, | ||
50 | 0, 0, 0, 0, 0, 0, 0, 0, | ||
51 | 0, 0, 0, 0, 0, 0, 0, 0, | ||
52 | 0, 0, 0, 0, 0, 0, 0, 0, | ||
53 | 0, 0, 0, 0, 0, 0, 0, 0, | ||
54 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 | ||
55 | } | ||
56 | }, | ||
57 | [SMS_IR_KB_HCW_SILVER] = { | ||
58 | .ir_protocol = IR_RC5, | ||
59 | .rc5_kbd_address = KEYBOARD_ADDRESS_LIGHTING1, | ||
60 | .keyboard_layout_map = { | ||
61 | KEY_0, KEY_1, KEY_2, | ||
62 | KEY_3, KEY_4, KEY_5, | ||
63 | KEY_6, KEY_7, KEY_8, | ||
64 | KEY_9, KEY_TEXT, KEY_RED, | ||
65 | KEY_RADIO, KEY_MENU, | ||
66 | KEY_SUBTITLE, | ||
67 | KEY_MUTE, KEY_VOLUMEUP, | ||
68 | KEY_VOLUMEDOWN, KEY_PREVIOUS, 0, | ||
69 | KEY_UP, KEY_DOWN, KEY_LEFT, | ||
70 | KEY_RIGHT, KEY_VIDEO, KEY_AUDIO, | ||
71 | KEY_MHP, KEY_EPG, KEY_TV, | ||
72 | 0, KEY_NEXTSONG, KEY_EXIT, | ||
73 | KEY_CHANNELUP, KEY_CHANNELDOWN, | ||
74 | KEY_CHANNEL, 0, | ||
75 | KEY_PREVIOUSSONG, KEY_ENTER, | ||
76 | KEY_SLEEP, 0, 0, KEY_BLUE, | ||
77 | 0, 0, 0, 0, KEY_GREEN, 0, | ||
78 | KEY_PAUSE, 0, KEY_REWIND, | ||
79 | 0, KEY_FASTFORWARD, KEY_PLAY, | ||
80 | KEY_STOP, KEY_RECORD, | ||
81 | KEY_YELLOW, 0, 0, KEY_SELECT, | ||
82 | KEY_ZOOM, KEY_POWER, 0, 0 | ||
83 | } | ||
84 | }, | ||
85 | { } /* Terminating entry */ | ||
86 | }; | ||
87 | |||
88 | u32 ir_pos; | ||
89 | u32 ir_word; | ||
90 | u32 ir_toggle; | ||
91 | |||
92 | #define RC5_PUSH_BIT(dst, bit, pos) \ | ||
93 | { dst <<= 1; dst |= bit; pos++; } | ||
94 | |||
95 | |||
96 | static void sms_ir_rc5_event(struct smscore_device_t *coredev, | ||
97 | u32 toggle, u32 addr, u32 cmd) | ||
98 | { | ||
99 | bool toggle_changed; | ||
100 | u16 keycode; | ||
101 | |||
102 | sms_info("IR RC5 word: address %d, command %d, toggle %d", | ||
103 | addr, cmd, toggle); | ||
104 | |||
105 | toggle_changed = ir_toggle != toggle; | ||
106 | /* keep toggle */ | ||
107 | ir_toggle = toggle; | ||
108 | |||
109 | if (addr != | ||
110 | keyboard_layout_maps[coredev->ir.ir_kb_type].rc5_kbd_address) | ||
111 | return; /* Check for valid address */ | ||
112 | |||
113 | keycode = | ||
114 | keyboard_layout_maps | ||
115 | [coredev->ir.ir_kb_type].keyboard_layout_map[cmd]; | ||
116 | |||
117 | if (!toggle_changed && | ||
118 | (keycode != KEY_VOLUMEUP && keycode != KEY_VOLUMEDOWN)) | ||
119 | return; /* accept only repeated volume, reject other keys */ | ||
120 | |||
121 | sms_info("kernel input keycode (from ir) %d", keycode); | ||
122 | input_report_key(coredev->ir.input_dev, keycode, 1); | ||
123 | input_sync(coredev->ir.input_dev); | ||
124 | |||
125 | } | ||
126 | |||
127 | /* decode raw bit pattern to RC5 code */ | ||
128 | /* taken from ir-functions.c */ | ||
129 | static u32 ir_rc5_decode(unsigned int code) | ||
130 | { | ||
131 | /* unsigned int org_code = code;*/ | ||
132 | unsigned int pair; | ||
133 | unsigned int rc5 = 0; | ||
134 | int i; | ||
135 | |||
136 | for (i = 0; i < 14; ++i) { | ||
137 | pair = code & 0x3; | ||
138 | code >>= 2; | ||
139 | |||
140 | rc5 <<= 1; | ||
141 | switch (pair) { | ||
142 | case 0: | ||
143 | case 2: | ||
144 | break; | ||
145 | case 1: | ||
146 | rc5 |= 1; | ||
147 | break; | ||
148 | case 3: | ||
149 | /* dprintk(1, "ir-common: ir_rc5_decode(%x) bad code\n", org_code);*/ | ||
150 | sms_info("bad code"); | ||
151 | return 0; | ||
152 | } | ||
153 | } | ||
154 | /* | ||
155 | dprintk(1, "ir-common: code=%x, rc5=%x, start=%x, | ||
156 | toggle=%x, address=%x, " | ||
157 | "instr=%x\n", rc5, org_code, RC5_START(rc5), | ||
158 | RC5_TOGGLE(rc5), RC5_ADDR(rc5), RC5_INSTR(rc5)); | ||
159 | */ | ||
160 | return rc5; | ||
161 | } | ||
162 | |||
163 | static void sms_rc5_parse_word(struct smscore_device_t *coredev) | ||
164 | { | ||
165 | #define RC5_START(x) (((x)>>12)&3) | ||
166 | #define RC5_TOGGLE(x) (((x)>>11)&1) | ||
167 | #define RC5_ADDR(x) (((x)>>6)&0x1F) | ||
168 | #define RC5_INSTR(x) ((x)&0x3F) | ||
169 | |||
170 | int i, j; | ||
171 | u32 rc5_word = 0; | ||
172 | |||
173 | /* Reverse the IR word direction */ | ||
174 | for (i = 0 ; i < 28 ; i++) | ||
175 | RC5_PUSH_BIT(rc5_word, (ir_word>>i)&1, j) | ||
176 | |||
177 | rc5_word = ir_rc5_decode(rc5_word); | ||
178 | /* sms_info("temp = 0x%x, rc5_code = 0x%x", ir_word, rc5_word); */ | ||
179 | |||
180 | sms_ir_rc5_event(coredev, | ||
181 | RC5_TOGGLE(rc5_word), | ||
182 | RC5_ADDR(rc5_word), | ||
183 | RC5_INSTR(rc5_word)); | ||
184 | } | ||
185 | |||
186 | |||
187 | static void sms_rc5_accumulate_bits(struct smscore_device_t *coredev, | ||
188 | s32 ir_sample) | ||
189 | { | ||
190 | #define RC5_TIME_GRANULARITY 200 | ||
191 | #define RC5_DEF_BIT_TIME 889 | ||
192 | #define RC5_MAX_SAME_BIT_CONT 4 | ||
193 | #define RC5_WORD_LEN 27 /* 28 bit */ | ||
194 | |||
195 | u32 i, j; | ||
196 | s32 delta_time; | ||
197 | u32 time = (ir_sample > 0) ? ir_sample : (0-ir_sample); | ||
198 | u32 level = (ir_sample < 0) ? 0 : 1; | ||
199 | |||
200 | for (i = RC5_MAX_SAME_BIT_CONT; i > 0; i--) { | ||
201 | delta_time = time - (i*RC5_DEF_BIT_TIME) + RC5_TIME_GRANULARITY; | ||
202 | if (delta_time < 0) | ||
203 | continue; /* not so many consecutive bits */ | ||
204 | if (delta_time > (2 * RC5_TIME_GRANULARITY)) { | ||
205 | /* timeout */ | ||
206 | if (ir_pos == (RC5_WORD_LEN-1)) | ||
207 | /* complete last bit */ | ||
208 | RC5_PUSH_BIT(ir_word, level, ir_pos) | ||
209 | |||
210 | if (ir_pos == RC5_WORD_LEN) | ||
211 | sms_rc5_parse_word(coredev); | ||
212 | else if (ir_pos) /* timeout within a word */ | ||
213 | sms_info("IR error parsing a word"); | ||
214 | |||
215 | ir_pos = 0; | ||
216 | ir_word = 0; | ||
217 | /* sms_info("timeout %d", time); */ | ||
218 | break; | ||
219 | } | ||
220 | /* The time is within the range of this number of bits */ | ||
221 | for (j = 0 ; j < i ; j++) | ||
222 | RC5_PUSH_BIT(ir_word, level, ir_pos) | ||
223 | |||
224 | break; | ||
225 | } | ||
226 | } | ||
227 | |||
228 | void sms_ir_event(struct smscore_device_t *coredev, const char *buf, int len) | ||
229 | { | ||
230 | #define IR_DATA_RECEIVE_MAX_LEN 520 /* 128*4 + 4 + 4 */ | ||
231 | u32 i; | ||
232 | enum ir_protocol ir_protocol = | ||
233 | keyboard_layout_maps[coredev->ir.ir_kb_type] | ||
234 | .ir_protocol; | ||
235 | s32 *samples; | ||
236 | int count = len>>2; | ||
237 | |||
238 | samples = (s32 *)buf; | ||
239 | /* sms_info("IR buffer received, length = %d", count);*/ | ||
240 | |||
241 | for (i = 0; i < count; i++) | ||
242 | if (ir_protocol == IR_RC5) | ||
243 | sms_rc5_accumulate_bits(coredev, samples[i]); | ||
244 | /* IR_RCMM not implemented */ | ||
245 | } | ||
246 | |||
247 | int sms_ir_init(struct smscore_device_t *coredev) | ||
248 | { | ||
249 | struct input_dev *input_dev; | ||
250 | |||
251 | sms_info("Allocating input device"); | ||
252 | input_dev = input_allocate_device(); | ||
253 | if (!input_dev) { | ||
254 | sms_err("Not enough memory"); | ||
255 | return -ENOMEM; | ||
256 | } | ||
257 | |||
258 | coredev->ir.input_dev = input_dev; | ||
259 | coredev->ir.ir_kb_type = | ||
260 | sms_get_board(smscore_get_board_id(coredev))->ir_kb_type; | ||
261 | coredev->ir.keyboard_layout_map = | ||
262 | keyboard_layout_maps[coredev->ir.ir_kb_type]. | ||
263 | keyboard_layout_map; | ||
264 | sms_info("IR remote keyboard type is %d", coredev->ir.ir_kb_type); | ||
265 | |||
266 | coredev->ir.controller = 0; /* Todo: vega/nova SPI number */ | ||
267 | coredev->ir.timeout = IR_DEFAULT_TIMEOUT; | ||
268 | sms_info("IR port %d, timeout %d ms", | ||
269 | coredev->ir.controller, coredev->ir.timeout); | ||
270 | |||
271 | snprintf(coredev->ir.name, | ||
272 | IR_DEV_NAME_MAX_LEN, | ||
273 | "SMS IR w/kbd type %d", | ||
274 | coredev->ir.ir_kb_type); | ||
275 | input_dev->name = coredev->ir.name; | ||
276 | input_dev->phys = coredev->ir.name; | ||
277 | input_dev->dev.parent = coredev->device; | ||
278 | |||
279 | /* Key press events only */ | ||
280 | input_dev->evbit[0] = BIT_MASK(EV_KEY); | ||
281 | input_dev->keybit[BIT_WORD(BTN_0)] = BIT_MASK(BTN_0); | ||
282 | |||
283 | sms_info("Input device (IR) %s is set for key events", input_dev->name); | ||
284 | |||
285 | if (input_register_device(input_dev)) { | ||
286 | sms_err("Failed to register device"); | ||
287 | input_free_device(input_dev); | ||
288 | return -EACCES; | ||
289 | } | ||
290 | |||
291 | return 0; | ||
292 | } | ||
293 | |||
294 | void sms_ir_exit(struct smscore_device_t *coredev) | ||
295 | { | ||
296 | if (coredev->ir.input_dev) | ||
297 | input_unregister_device(coredev->ir.input_dev); | ||
298 | |||
299 | sms_info(""); | ||
300 | } | ||
301 | |||