]>
Commit | Line | Data |
---|---|---|
9d200153 SM |
1 | /* Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved. |
2 | * | |
3 | * This program is free software; you can redistribute it and/or modify | |
4 | * it under the terms of the GNU General Public License version 2 and | |
5 | * only version 2 as published by the Free Software Foundation. | |
6 | * | |
7 | * This program is distributed in the hope that it will be useful, | |
8 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
9 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
10 | * GNU General Public License for more details. | |
11 | * | |
12 | * You should have received a copy of the GNU General Public License | |
13 | * along with this program; if not, write to the Free Software | |
14 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | |
15 | * 02110-1301, USA. | |
16 | */ | |
17 | ||
18 | #include <linux/module.h> | |
19 | #include <linux/kernel.h> | |
20 | #include <linux/slab.h> | |
21 | #include <linux/delay.h> | |
22 | #include <linux/mm.h> | |
23 | #include <linux/fb.h> | |
24 | #include <linux/init.h> | |
25 | #include <linux/ioport.h> | |
26 | #include <linux/device.h> | |
27 | #include <linux/dma-mapping.h> | |
28 | ||
29 | #include "msm_fb_panel.h" | |
30 | #include "mddihost.h" | |
31 | #include "mddihosti.h" | |
32 | ||
33 | #define FEATURE_MDDI_UNDERRUN_RECOVERY | |
34 | #ifndef FEATURE_MDDI_DISABLE_REVERSE | |
35 | static void mddi_read_rev_packet(byte *data_ptr); | |
36 | #endif | |
37 | ||
38 | struct timer_list mddi_host_timer; | |
39 | ||
40 | #define MDDI_DEFAULT_TIMER_LENGTH 5000 /* 5 seconds */ | |
41 | uint32 mddi_rtd_frequency = 60000; /* send RTD every 60 seconds */ | |
42 | uint32 mddi_client_status_frequency = 60000; /* get status pkt every 60 secs */ | |
43 | ||
44 | boolean mddi_vsync_detect_enabled = FALSE; | |
45 | mddi_gpio_info_type mddi_gpio; | |
46 | ||
47 | uint32 mddi_host_core_version; | |
48 | boolean mddi_debug_log_statistics = FALSE; | |
49 | /* #define FEATURE_MDDI_HOST_ENABLE_EARLY_HIBERNATION */ | |
50 | /* default to TRUE in case MDP does not vote */ | |
51 | static boolean mddi_host_mdp_active_flag = TRUE; | |
52 | static uint32 mddi_log_stats_counter; | |
53 | uint32 mddi_log_stats_frequency = 4000; | |
54 | ||
55 | #define MDDI_DEFAULT_REV_PKT_SIZE 0x20 | |
56 | ||
57 | #ifndef FEATURE_MDDI_DISABLE_REVERSE | |
58 | static boolean mddi_rev_ptr_workaround = TRUE; | |
59 | static uint32 mddi_reg_read_retry; | |
60 | static uint32 mddi_reg_read_retry_max = 20; | |
61 | static boolean mddi_enable_reg_read_retry = TRUE; | |
62 | static boolean mddi_enable_reg_read_retry_once = FALSE; | |
63 | ||
64 | #define MDDI_MAX_REV_PKT_SIZE 0x60 | |
65 | ||
66 | #define MDDI_CLIENT_CAPABILITY_REV_PKT_SIZE 0x60 | |
67 | ||
68 | #define MDDI_VIDEO_REV_PKT_SIZE 0x40 | |
69 | #define MDDI_REV_BUFFER_SIZE MDDI_MAX_REV_PKT_SIZE | |
70 | static byte rev_packet_data[MDDI_MAX_REV_PKT_SIZE]; | |
71 | #endif /* FEATURE_MDDI_DISABLE_REVERSE */ | |
72 | /* leave these variables so graphics will compile */ | |
73 | ||
74 | #define MDDI_MAX_REV_DATA_SIZE 128 | |
75 | /*lint -d__align(x) */ | |
76 | boolean mddi_debug_clear_rev_data = TRUE; | |
77 | ||
78 | uint32 *mddi_reg_read_value_ptr; | |
79 | ||
80 | mddi_client_capability_type mddi_client_capability_pkt; | |
81 | static boolean mddi_client_capability_request = FALSE; | |
82 | ||
83 | #ifndef FEATURE_MDDI_DISABLE_REVERSE | |
84 | ||
85 | #define MAX_MDDI_REV_HANDLERS 2 | |
86 | #define INVALID_PKT_TYPE 0xFFFF | |
87 | ||
88 | typedef struct { | |
89 | mddi_rev_handler_type handler; /* ISR to be executed */ | |
90 | uint16 pkt_type; | |
91 | } mddi_rev_pkt_handler_type; | |
92 | static mddi_rev_pkt_handler_type mddi_rev_pkt_handler[MAX_MDDI_REV_HANDLERS] = | |
93 | { {NULL, INVALID_PKT_TYPE}, {NULL, INVALID_PKT_TYPE} }; | |
94 | ||
95 | static boolean mddi_rev_encap_user_request = FALSE; | |
96 | static mddi_linked_list_notify_type mddi_rev_user; | |
97 | ||
98 | spinlock_t mddi_host_spin_lock; | |
99 | extern uint32 mdp_in_processing; | |
100 | #endif | |
101 | ||
102 | typedef enum { | |
103 | MDDI_REV_IDLE | |
104 | #ifndef FEATURE_MDDI_DISABLE_REVERSE | |
105 | , MDDI_REV_REG_READ_ISSUED, | |
106 | MDDI_REV_REG_READ_SENT, | |
107 | MDDI_REV_ENCAP_ISSUED, | |
108 | MDDI_REV_STATUS_REQ_ISSUED, | |
109 | MDDI_REV_CLIENT_CAP_ISSUED | |
110 | #endif | |
111 | } mddi_rev_link_state_type; | |
112 | ||
113 | typedef enum { | |
114 | MDDI_LINK_DISABLED, | |
115 | MDDI_LINK_HIBERNATING, | |
116 | MDDI_LINK_ACTIVATING, | |
117 | MDDI_LINK_ACTIVE | |
118 | } mddi_host_link_state_type; | |
119 | ||
120 | typedef struct { | |
121 | uint32 count; | |
122 | uint32 in_count; | |
123 | uint32 disp_req_count; | |
124 | uint32 state_change_count; | |
125 | uint32 ll_done_count; | |
126 | uint32 rev_avail_count; | |
127 | uint32 error_count; | |
128 | uint32 rev_encap_count; | |
129 | uint32 llist_ptr_write_1; | |
130 | uint32 llist_ptr_write_2; | |
131 | } mddi_host_int_type; | |
132 | ||
133 | typedef struct { | |
134 | uint32 fwd_crc_count; | |
135 | uint32 rev_crc_count; | |
136 | uint32 pri_underflow; | |
137 | uint32 sec_underflow; | |
138 | uint32 rev_overflow; | |
139 | uint32 pri_overwrite; | |
140 | uint32 sec_overwrite; | |
141 | uint32 rev_overwrite; | |
142 | uint32 dma_failure; | |
143 | uint32 rtd_failure; | |
144 | uint32 reg_read_failure; | |
145 | #ifdef FEATURE_MDDI_UNDERRUN_RECOVERY | |
146 | uint32 pri_underrun_detected; | |
147 | #endif | |
148 | } mddi_host_stat_type; | |
149 | ||
150 | typedef struct { | |
151 | uint32 rtd_cnt; | |
152 | uint32 rev_enc_cnt; | |
153 | uint32 vid_cnt; | |
154 | uint32 reg_acc_cnt; | |
155 | uint32 cli_stat_cnt; | |
156 | uint32 cli_cap_cnt; | |
157 | uint32 reg_read_cnt; | |
158 | uint32 link_active_cnt; | |
159 | uint32 link_hibernate_cnt; | |
160 | uint32 vsync_response_cnt; | |
161 | uint32 fwd_crc_cnt; | |
162 | uint32 rev_crc_cnt; | |
163 | } mddi_log_params_struct_type; | |
164 | ||
165 | typedef struct { | |
166 | uint32 rtd_value; | |
167 | uint32 rtd_counter; | |
168 | uint32 client_status_cnt; | |
169 | boolean rev_ptr_written; | |
170 | uint8 *rev_ptr_start; | |
171 | uint8 *rev_ptr_curr; | |
172 | uint32 mddi_rev_ptr_write_val; | |
173 | dma_addr_t rev_data_dma_addr; | |
174 | uint16 rev_pkt_size; | |
175 | mddi_rev_link_state_type rev_state; | |
176 | mddi_host_link_state_type link_state; | |
177 | mddi_host_driver_state_type driver_state; | |
178 | boolean disable_hibernation; | |
179 | uint32 saved_int_reg; | |
180 | uint32 saved_int_en; | |
181 | mddi_linked_list_type *llist_ptr; | |
182 | dma_addr_t llist_dma_addr; | |
183 | mddi_linked_list_type *llist_dma_ptr; | |
184 | uint32 *rev_data_buf; | |
185 | struct completion mddi_llist_avail_comp; | |
186 | boolean mddi_waiting_for_llist_avail; | |
187 | mddi_host_int_type int_type; | |
188 | mddi_host_stat_type stats; | |
189 | mddi_log_params_struct_type log_parms; | |
190 | mddi_llist_info_type llist_info; | |
191 | mddi_linked_list_notify_type llist_notify[MDDI_MAX_NUM_LLIST_ITEMS]; | |
192 | } mddi_host_cntl_type; | |
193 | ||
194 | static mddi_host_type mddi_curr_host = MDDI_HOST_PRIM; | |
195 | static mddi_host_cntl_type mhctl[MDDI_NUM_HOST_CORES]; | |
196 | mddi_linked_list_type *llist_extern[MDDI_NUM_HOST_CORES]; | |
197 | mddi_linked_list_type *llist_dma_extern[MDDI_NUM_HOST_CORES]; | |
198 | mddi_linked_list_notify_type *llist_extern_notify[MDDI_NUM_HOST_CORES]; | |
199 | static mddi_log_params_struct_type prev_parms[MDDI_NUM_HOST_CORES]; | |
200 | ||
201 | extern uint32 mdp_total_vdopkts; | |
202 | ||
203 | static boolean mddi_host_io_clock_on = FALSE; | |
204 | static boolean mddi_host_hclk_on = FALSE; | |
205 | ||
206 | int int_mddi_pri_flag = FALSE; | |
207 | int int_mddi_ext_flag = FALSE; | |
208 | ||
209 | static void mddi_report_errors(uint32 int_reg) | |
210 | { | |
211 | mddi_host_type host_idx = mddi_curr_host; | |
212 | mddi_host_cntl_type *pmhctl = &(mhctl[host_idx]); | |
213 | ||
214 | if (int_reg & MDDI_INT_PRI_UNDERFLOW) { | |
215 | pmhctl->stats.pri_underflow++; | |
216 | MDDI_MSG_ERR("!!! MDDI Primary Underflow !!!\n"); | |
217 | } | |
218 | if (int_reg & MDDI_INT_SEC_UNDERFLOW) { | |
219 | pmhctl->stats.sec_underflow++; | |
220 | MDDI_MSG_ERR("!!! MDDI Secondary Underflow !!!\n"); | |
221 | } | |
222 | #ifndef FEATURE_MDDI_DISABLE_REVERSE | |
223 | if (int_reg & MDDI_INT_REV_OVERFLOW) { | |
224 | pmhctl->stats.rev_overflow++; | |
225 | MDDI_MSG_ERR("!!! MDDI Reverse Overflow !!!\n"); | |
226 | pmhctl->rev_ptr_curr = pmhctl->rev_ptr_start; | |
227 | mddi_host_reg_out(REV_PTR, pmhctl->mddi_rev_ptr_write_val); | |
228 | ||
229 | } | |
230 | if (int_reg & MDDI_INT_CRC_ERROR) | |
231 | MDDI_MSG_ERR("!!! MDDI Reverse CRC Error !!!\n"); | |
232 | #endif | |
233 | if (int_reg & MDDI_INT_PRI_OVERWRITE) { | |
234 | pmhctl->stats.pri_overwrite++; | |
235 | MDDI_MSG_ERR("!!! MDDI Primary Overwrite !!!\n"); | |
236 | } | |
237 | if (int_reg & MDDI_INT_SEC_OVERWRITE) { | |
238 | pmhctl->stats.sec_overwrite++; | |
239 | MDDI_MSG_ERR("!!! MDDI Secondary Overwrite !!!\n"); | |
240 | } | |
241 | #ifndef FEATURE_MDDI_DISABLE_REVERSE | |
242 | if (int_reg & MDDI_INT_REV_OVERWRITE) { | |
243 | pmhctl->stats.rev_overwrite++; | |
244 | /* This will show up normally and is not a problem */ | |
245 | MDDI_MSG_DEBUG("MDDI Reverse Overwrite!\n"); | |
246 | } | |
247 | if (int_reg & MDDI_INT_RTD_FAILURE) { | |
248 | mddi_host_reg_outm(INTEN, MDDI_INT_RTD_FAILURE, 0); | |
249 | pmhctl->stats.rtd_failure++; | |
250 | MDDI_MSG_ERR("!!! MDDI RTD Failure !!!\n"); | |
251 | } | |
252 | #endif | |
253 | if (int_reg & MDDI_INT_DMA_FAILURE) { | |
254 | pmhctl->stats.dma_failure++; | |
255 | MDDI_MSG_ERR("!!! MDDI DMA Abort !!!\n"); | |
256 | } | |
257 | } | |
258 | ||
259 | static void mddi_host_enable_io_clock(void) | |
260 | { | |
261 | if (!MDDI_HOST_IS_IO_CLOCK_ON) | |
262 | MDDI_HOST_ENABLE_IO_CLOCK; | |
263 | } | |
264 | ||
265 | static void mddi_host_enable_hclk(void) | |
266 | { | |
267 | ||
268 | if (!MDDI_HOST_IS_HCLK_ON) | |
269 | MDDI_HOST_ENABLE_HCLK; | |
270 | } | |
271 | ||
272 | static void mddi_host_disable_io_clock(void) | |
273 | { | |
274 | #ifndef FEATURE_MDDI_HOST_IO_CLOCK_CONTROL_DISABLE | |
275 | if (MDDI_HOST_IS_IO_CLOCK_ON) | |
276 | MDDI_HOST_DISABLE_IO_CLOCK; | |
277 | #endif | |
278 | } | |
279 | ||
280 | static void mddi_host_disable_hclk(void) | |
281 | { | |
282 | #ifndef FEATURE_MDDI_HOST_HCLK_CONTROL_DISABLE | |
283 | if (MDDI_HOST_IS_HCLK_ON) | |
284 | MDDI_HOST_DISABLE_HCLK; | |
285 | #endif | |
286 | } | |
287 | ||
288 | static void mddi_vote_to_sleep(mddi_host_type host_idx, boolean sleep) | |
289 | { | |
290 | uint16 vote_mask; | |
291 | ||
292 | if (host_idx == MDDI_HOST_PRIM) | |
293 | vote_mask = 0x01; | |
294 | else | |
295 | vote_mask = 0x02; | |
296 | } | |
297 | ||
298 | static void mddi_report_state_change(uint32 int_reg) | |
299 | { | |
300 | mddi_host_type host_idx = mddi_curr_host; | |
301 | mddi_host_cntl_type *pmhctl = &(mhctl[host_idx]); | |
302 | ||
303 | if ((pmhctl->saved_int_reg & MDDI_INT_IN_HIBERNATION) && | |
304 | (pmhctl->saved_int_reg & MDDI_INT_LINK_ACTIVE)) { | |
305 | /* recover from condition where the io_clock was turned off by the | |
306 | clock driver during a transition to hibernation. The io_clock | |
307 | disable is to prevent MDP/MDDI underruns when changing ARM | |
308 | clock speeds. In the process of halting the ARM, the hclk | |
309 | divider needs to be set to 1. When it is set to 1, there is | |
310 | a small time (usecs) when hclk is off or slow, and this can | |
311 | cause an underrun. To prevent the underrun, clock driver turns | |
312 | off the MDDI io_clock before making the change. */ | |
313 | mddi_host_reg_out(CMD, MDDI_CMD_POWERUP); | |
314 | } | |
315 | ||
316 | if (int_reg & MDDI_INT_LINK_ACTIVE) { | |
317 | pmhctl->link_state = MDDI_LINK_ACTIVE; | |
318 | pmhctl->log_parms.link_active_cnt++; | |
319 | pmhctl->rtd_value = mddi_host_reg_in(RTD_VAL); | |
320 | MDDI_MSG_DEBUG("!!! MDDI Active RTD:0x%x!!!\n", | |
321 | pmhctl->rtd_value); | |
322 | /* now interrupt on hibernation */ | |
323 | mddi_host_reg_outm(INTEN, | |
324 | (MDDI_INT_IN_HIBERNATION | | |
325 | MDDI_INT_LINK_ACTIVE), | |
326 | MDDI_INT_IN_HIBERNATION); | |
327 | ||
328 | #ifdef DEBUG_MDDIHOSTI | |
329 | /* if gpio interrupt is enabled, start polling at fastest | |
330 | * registered rate | |
331 | */ | |
332 | if (mddi_gpio.polling_enabled) { | |
333 | timer_reg(&mddi_gpio_poll_timer, | |
334 | mddi_gpio_poll_timer_cb, 0, mddi_gpio.polling_interval, 0); | |
335 | } | |
336 | #endif | |
337 | #ifndef FEATURE_MDDI_DISABLE_REVERSE | |
338 | if (mddi_rev_ptr_workaround) { | |
339 | /* HW CR: need to reset reverse register stuff */ | |
340 | pmhctl->rev_ptr_written = FALSE; | |
341 | pmhctl->rev_ptr_curr = pmhctl->rev_ptr_start; | |
342 | } | |
343 | #endif | |
344 | /* vote on sleep */ | |
345 | mddi_vote_to_sleep(host_idx, FALSE); | |
346 | ||
347 | if (host_idx == MDDI_HOST_PRIM) { | |
348 | if (mddi_vsync_detect_enabled) { | |
349 | /* | |
350 | * Indicate to client specific code that vsync | |
351 | * was enabled, but we did not detect a client | |
352 | * intiated wakeup. The client specific | |
353 | * handler can either reassert vsync detection, | |
354 | * or treat this as a valid vsync. | |
355 | */ | |
356 | mddi_client_lcd_vsync_detected(FALSE); | |
357 | pmhctl->log_parms.vsync_response_cnt++; | |
358 | } | |
359 | } | |
360 | } | |
361 | if (int_reg & MDDI_INT_IN_HIBERNATION) { | |
362 | pmhctl->link_state = MDDI_LINK_HIBERNATING; | |
363 | pmhctl->log_parms.link_hibernate_cnt++; | |
364 | MDDI_MSG_DEBUG("!!! MDDI Hibernating !!!\n"); | |
365 | /* now interrupt on link_active */ | |
366 | #ifdef FEATURE_MDDI_DISABLE_REVERSE | |
367 | mddi_host_reg_outm(INTEN, | |
368 | (MDDI_INT_MDDI_IN | | |
369 | MDDI_INT_IN_HIBERNATION | | |
370 | MDDI_INT_LINK_ACTIVE), | |
371 | MDDI_INT_LINK_ACTIVE); | |
372 | #else | |
373 | mddi_host_reg_outm(INTEN, | |
374 | (MDDI_INT_MDDI_IN | | |
375 | MDDI_INT_IN_HIBERNATION | | |
376 | MDDI_INT_LINK_ACTIVE), | |
377 | (MDDI_INT_MDDI_IN | MDDI_INT_LINK_ACTIVE)); | |
378 | ||
379 | pmhctl->rtd_counter = mddi_rtd_frequency; | |
380 | ||
381 | if (pmhctl->rev_state != MDDI_REV_IDLE) { | |
382 | /* a rev_encap will not wake up the link, so we do that here */ | |
383 | pmhctl->link_state = MDDI_LINK_ACTIVATING; | |
384 | mddi_host_reg_out(CMD, MDDI_CMD_LINK_ACTIVE); | |
385 | } | |
386 | #endif | |
387 | ||
388 | if (pmhctl->disable_hibernation) { | |
389 | mddi_host_reg_out(CMD, MDDI_CMD_HIBERNATE); | |
390 | mddi_host_reg_out(CMD, MDDI_CMD_LINK_ACTIVE); | |
391 | pmhctl->link_state = MDDI_LINK_ACTIVATING; | |
392 | } | |
393 | #ifdef FEATURE_MDDI_UNDERRUN_RECOVERY | |
394 | if ((pmhctl->llist_info.transmitting_start_idx != | |
395 | UNASSIGNED_INDEX) | |
396 | && | |
397 | ((pmhctl-> | |
398 | saved_int_reg & (MDDI_INT_PRI_LINK_LIST_DONE | | |
399 | MDDI_INT_PRI_PTR_READ)) == | |
400 | MDDI_INT_PRI_PTR_READ)) { | |
401 | mddi_linked_list_type *llist_dma; | |
402 | llist_dma = pmhctl->llist_dma_ptr; | |
403 | /* | |
404 | * All indications are that we have not received a | |
405 | * linked list done interrupt, due to an underrun | |
406 | * condition. Recovery attempt is to send again. | |
407 | */ | |
408 | dma_coherent_pre_ops(); | |
409 | /* Write to primary pointer register again */ | |
410 | mddi_host_reg_out(PRI_PTR, | |
411 | &llist_dma[pmhctl->llist_info. | |
412 | transmitting_start_idx]); | |
413 | pmhctl->stats.pri_underrun_detected++; | |
414 | } | |
415 | #endif | |
416 | ||
417 | /* vote on sleep */ | |
418 | if (pmhctl->link_state == MDDI_LINK_HIBERNATING) { | |
419 | mddi_vote_to_sleep(host_idx, TRUE); | |
420 | } | |
421 | ||
422 | #ifdef DEBUG_MDDIHOSTI | |
423 | /* need to stop polling timer */ | |
424 | if (mddi_gpio.polling_enabled) { | |
425 | (void) timer_clr(&mddi_gpio_poll_timer, T_NONE); | |
426 | } | |
427 | #endif | |
428 | } | |
429 | } | |
430 | ||
431 | void mddi_host_timer_service(unsigned long data) | |
432 | { | |
433 | #ifndef FEATURE_MDDI_DISABLE_REVERSE | |
434 | unsigned long flags; | |
435 | #endif | |
436 | mddi_host_type host_idx; | |
437 | mddi_host_cntl_type *pmhctl; | |
438 | ||
439 | unsigned long time_ms = MDDI_DEFAULT_TIMER_LENGTH; | |
440 | init_timer(&mddi_host_timer); | |
441 | mddi_host_timer.function = mddi_host_timer_service; | |
442 | mddi_host_timer.data = 0; | |
443 | ||
444 | mddi_host_timer.expires = jiffies + ((time_ms * HZ) / 1000); | |
445 | add_timer(&mddi_host_timer); | |
446 | ||
447 | for (host_idx = MDDI_HOST_PRIM; host_idx < MDDI_NUM_HOST_CORES; | |
448 | host_idx++) { | |
449 | pmhctl = &(mhctl[host_idx]); | |
450 | mddi_log_stats_counter += (uint32) time_ms; | |
451 | #ifndef FEATURE_MDDI_DISABLE_REVERSE | |
452 | pmhctl->rtd_counter += (uint32) time_ms; | |
453 | pmhctl->client_status_cnt += (uint32) time_ms; | |
454 | ||
455 | if (host_idx == MDDI_HOST_PRIM) { | |
456 | if (pmhctl->client_status_cnt >= | |
457 | mddi_client_status_frequency) { | |
458 | if ((pmhctl->link_state == | |
459 | MDDI_LINK_HIBERNATING) | |
460 | && (pmhctl->client_status_cnt > | |
461 | mddi_client_status_frequency)) { | |
462 | /* | |
463 | * special case where we are hibernating | |
464 | * and mddi_host_isr is not firing, so | |
465 | * kick the link so that the status can | |
466 | * be retrieved | |
467 | */ | |
468 | ||
469 | /* need to wake up link before issuing | |
470 | * rev encap command | |
471 | */ | |
472 | MDDI_MSG_INFO("wake up link!\n"); | |
473 | spin_lock_irqsave(&mddi_host_spin_lock, | |
474 | flags); | |
475 | mddi_host_enable_hclk(); | |
476 | mddi_host_enable_io_clock(); | |
477 | pmhctl->link_state = | |
478 | MDDI_LINK_ACTIVATING; | |
479 | mddi_host_reg_out(CMD, | |
480 | MDDI_CMD_LINK_ACTIVE); | |
481 | spin_unlock_irqrestore | |
482 | (&mddi_host_spin_lock, flags); | |
483 | } else | |
484 | if ((pmhctl->link_state == MDDI_LINK_ACTIVE) | |
485 | && pmhctl->disable_hibernation) { | |
486 | /* | |
487 | * special case where we have disabled | |
488 | * hibernation and mddi_host_isr | |
489 | * is not firing, so enable interrupt | |
490 | * for no pkts pending, which will | |
491 | * generate an interrupt | |
492 | */ | |
493 | MDDI_MSG_INFO("kick isr!\n"); | |
494 | spin_lock_irqsave(&mddi_host_spin_lock, | |
495 | flags); | |
496 | mddi_host_enable_hclk(); | |
497 | mddi_host_reg_outm(INTEN, | |
498 | MDDI_INT_NO_CMD_PKTS_PEND, | |
499 | MDDI_INT_NO_CMD_PKTS_PEND); | |
500 | spin_unlock_irqrestore | |
501 | (&mddi_host_spin_lock, flags); | |
502 | } | |
503 | } | |
504 | } | |
505 | #endif /* #ifndef FEATURE_MDDI_DISABLE_REVERSE */ | |
506 | } | |
507 | ||
508 | /* Check if logging is turned on */ | |
509 | for (host_idx = MDDI_HOST_PRIM; host_idx < MDDI_NUM_HOST_CORES; | |
510 | host_idx++) { | |
511 | mddi_log_params_struct_type *prev_ptr = &(prev_parms[host_idx]); | |
512 | pmhctl = &(mhctl[host_idx]); | |
513 | ||
514 | if (mddi_debug_log_statistics) { | |
515 | ||
516 | /* get video pkt count from MDP, since MDDI sw cannot know this */ | |
517 | pmhctl->log_parms.vid_cnt = mdp_total_vdopkts; | |
518 | ||
519 | if (mddi_log_stats_counter >= mddi_log_stats_frequency) { | |
520 | /* mddi_log_stats_counter = 0; */ | |
521 | if (mddi_debug_log_statistics) { | |
522 | MDDI_MSG_NOTICE | |
523 | ("MDDI Statistics since last report:\n"); | |
524 | MDDI_MSG_NOTICE(" Packets sent:\n"); | |
525 | MDDI_MSG_NOTICE | |
526 | (" %d RTD packet(s)\n", | |
527 | pmhctl->log_parms.rtd_cnt - | |
528 | prev_ptr->rtd_cnt); | |
529 | if (prev_ptr->rtd_cnt != | |
530 | pmhctl->log_parms.rtd_cnt) { | |
531 | unsigned long flags; | |
532 | spin_lock_irqsave | |
533 | (&mddi_host_spin_lock, | |
534 | flags); | |
535 | mddi_host_enable_hclk(); | |
536 | pmhctl->rtd_value = | |
537 | mddi_host_reg_in(RTD_VAL); | |
538 | spin_unlock_irqrestore | |
539 | (&mddi_host_spin_lock, | |
540 | flags); | |
541 | MDDI_MSG_NOTICE | |
542 | (" RTD value=%d\n", | |
543 | pmhctl->rtd_value); | |
544 | } | |
545 | MDDI_MSG_NOTICE | |
546 | (" %d VIDEO packets\n", | |
547 | pmhctl->log_parms.vid_cnt - | |
548 | prev_ptr->vid_cnt); | |
549 | MDDI_MSG_NOTICE | |
550 | (" %d Register Access packets\n", | |
551 | pmhctl->log_parms.reg_acc_cnt - | |
552 | prev_ptr->reg_acc_cnt); | |
553 | MDDI_MSG_NOTICE | |
554 | (" %d Reverse Encapsulation packet(s)\n", | |
555 | pmhctl->log_parms.rev_enc_cnt - | |
556 | prev_ptr->rev_enc_cnt); | |
557 | if (prev_ptr->rev_enc_cnt != | |
558 | pmhctl->log_parms.rev_enc_cnt) { | |
559 | /* report # of reverse CRC errors */ | |
560 | MDDI_MSG_NOTICE | |
561 | (" %d reverse CRC errors detected\n", | |
562 | pmhctl->log_parms. | |
563 | rev_crc_cnt - | |
564 | prev_ptr->rev_crc_cnt); | |
565 | } | |
566 | MDDI_MSG_NOTICE | |
567 | (" Packets received:\n"); | |
568 | MDDI_MSG_NOTICE | |
569 | (" %d Client Status packets", | |
570 | pmhctl->log_parms.cli_stat_cnt - | |
571 | prev_ptr->cli_stat_cnt); | |
572 | if (prev_ptr->cli_stat_cnt != | |
573 | pmhctl->log_parms.cli_stat_cnt) { | |
574 | MDDI_MSG_NOTICE | |
575 | (" %d forward CRC errors reported\n", | |
576 | pmhctl->log_parms. | |
577 | fwd_crc_cnt - | |
578 | prev_ptr->fwd_crc_cnt); | |
579 | } | |
580 | MDDI_MSG_NOTICE | |
581 | (" %d Register Access Read packets\n", | |
582 | pmhctl->log_parms.reg_read_cnt - | |
583 | prev_ptr->reg_read_cnt); | |
584 | ||
585 | if (pmhctl->link_state == | |
586 | MDDI_LINK_ACTIVE) { | |
587 | MDDI_MSG_NOTICE | |
588 | (" Current Link Status: Active\n"); | |
589 | } else | |
590 | if ((pmhctl->link_state == | |
591 | MDDI_LINK_HIBERNATING) | |
592 | || (pmhctl->link_state == | |
593 | MDDI_LINK_ACTIVATING)) { | |
594 | MDDI_MSG_NOTICE | |
595 | (" Current Link Status: Hibernation\n"); | |
596 | } else { | |
597 | MDDI_MSG_NOTICE | |
598 | (" Current Link Status: Inactive\n"); | |
599 | } | |
600 | MDDI_MSG_NOTICE | |
601 | (" Active state entered %d times\n", | |
602 | pmhctl->log_parms.link_active_cnt - | |
603 | prev_ptr->link_active_cnt); | |
604 | MDDI_MSG_NOTICE | |
605 | (" Hibernation state entered %d times\n", | |
606 | pmhctl->log_parms. | |
607 | link_hibernate_cnt - | |
608 | prev_ptr->link_hibernate_cnt); | |
609 | } | |
610 | } | |
611 | prev_parms[host_idx] = pmhctl->log_parms; | |
612 | } | |
613 | } | |
614 | if (mddi_log_stats_counter >= mddi_log_stats_frequency) | |
615 | mddi_log_stats_counter = 0; | |
616 | ||
617 | return; | |
618 | } /* mddi_host_timer_cb */ | |
619 | ||
620 | static void mddi_process_link_list_done(void) | |
621 | { | |
622 | mddi_host_type host_idx = mddi_curr_host; | |
623 | mddi_host_cntl_type *pmhctl = &(mhctl[host_idx]); | |
624 | ||
625 | /* normal forward linked list packet(s) were sent */ | |
626 | if (pmhctl->llist_info.transmitting_start_idx == UNASSIGNED_INDEX) { | |
627 | MDDI_MSG_ERR("**** getting LL done, but no list ****\n"); | |
628 | } else { | |
629 | uint16 idx; | |
630 | ||
631 | #ifndef FEATURE_MDDI_DISABLE_REVERSE | |
632 | if (pmhctl->rev_state == MDDI_REV_REG_READ_ISSUED) { | |
633 | /* special case where a register read packet was sent */ | |
634 | pmhctl->rev_state = MDDI_REV_REG_READ_SENT; | |
635 | if (pmhctl->llist_info.reg_read_idx == UNASSIGNED_INDEX) { | |
636 | MDDI_MSG_ERR | |
637 | ("**** getting LL done, but no list ****\n"); | |
638 | } | |
639 | } | |
640 | #endif | |
641 | for (idx = pmhctl->llist_info.transmitting_start_idx;;) { | |
642 | uint16 next_idx = pmhctl->llist_notify[idx].next_idx; | |
643 | /* with reg read we don't release the waiting tcb until after | |
644 | * the reverse encapsulation has completed. | |
645 | */ | |
646 | if (idx != pmhctl->llist_info.reg_read_idx) { | |
647 | /* notify task that may be waiting on this completion */ | |
648 | if (pmhctl->llist_notify[idx].waiting) { | |
649 | complete(& | |
650 | (pmhctl->llist_notify[idx]. | |
651 | done_comp)); | |
652 | } | |
653 | if (pmhctl->llist_notify[idx].done_cb != NULL) { | |
654 | (*(pmhctl->llist_notify[idx].done_cb)) | |
655 | (); | |
656 | } | |
657 | ||
658 | pmhctl->llist_notify[idx].in_use = FALSE; | |
659 | pmhctl->llist_notify[idx].waiting = FALSE; | |
660 | pmhctl->llist_notify[idx].done_cb = NULL; | |
661 | if (idx < MDDI_NUM_DYNAMIC_LLIST_ITEMS) { | |
662 | /* static LLIST items are configured only once */ | |
663 | pmhctl->llist_notify[idx].next_idx = | |
664 | UNASSIGNED_INDEX; | |
665 | } | |
666 | /* | |
667 | * currently, all linked list packets are | |
668 | * register access, so we can increment the | |
669 | * counter for that packet type here. | |
670 | */ | |
671 | pmhctl->log_parms.reg_acc_cnt++; | |
672 | } | |
673 | if (idx == pmhctl->llist_info.transmitting_end_idx) | |
674 | break; | |
675 | idx = next_idx; | |
676 | if (idx == UNASSIGNED_INDEX) | |
677 | MDDI_MSG_CRIT("MDDI linked list corruption!\n"); | |
678 | } | |
679 | ||
680 | pmhctl->llist_info.transmitting_start_idx = UNASSIGNED_INDEX; | |
681 | pmhctl->llist_info.transmitting_end_idx = UNASSIGNED_INDEX; | |
682 | ||
683 | if (pmhctl->mddi_waiting_for_llist_avail) { | |
684 | if (! | |
685 | (pmhctl-> | |
686 | llist_notify[pmhctl->llist_info.next_free_idx]. | |
687 | in_use)) { | |
688 | pmhctl->mddi_waiting_for_llist_avail = FALSE; | |
689 | complete(&(pmhctl->mddi_llist_avail_comp)); | |
690 | } | |
691 | } | |
692 | } | |
693 | ||
694 | /* Turn off MDDI_INT_PRI_LINK_LIST_DONE interrupt */ | |
695 | mddi_host_reg_outm(INTEN, MDDI_INT_PRI_LINK_LIST_DONE, 0); | |
696 | ||
697 | } | |
698 | ||
699 | static void mddi_queue_forward_linked_list(void) | |
700 | { | |
701 | uint16 first_pkt_index; | |
702 | mddi_linked_list_type *llist_dma; | |
703 | mddi_host_type host_idx = mddi_curr_host; | |
704 | mddi_host_cntl_type *pmhctl = &(mhctl[host_idx]); | |
705 | llist_dma = pmhctl->llist_dma_ptr; | |
706 | ||
707 | first_pkt_index = UNASSIGNED_INDEX; | |
708 | ||
709 | if (pmhctl->llist_info.transmitting_start_idx == UNASSIGNED_INDEX) { | |
710 | #ifndef FEATURE_MDDI_DISABLE_REVERSE | |
711 | if (pmhctl->llist_info.reg_read_waiting) { | |
712 | if (pmhctl->rev_state == MDDI_REV_IDLE) { | |
713 | /* | |
714 | * we have a register read to send and | |
715 | * can send it now | |
716 | */ | |
717 | pmhctl->rev_state = MDDI_REV_REG_READ_ISSUED; | |
718 | mddi_reg_read_retry = 0; | |
719 | first_pkt_index = | |
720 | pmhctl->llist_info.waiting_start_idx; | |
721 | pmhctl->llist_info.reg_read_waiting = FALSE; | |
722 | } | |
723 | } else | |
724 | #endif | |
725 | { | |
726 | /* | |
727 | * not register read to worry about, go ahead and write | |
728 | * anything that may be on the waiting list. | |
729 | */ | |
730 | first_pkt_index = pmhctl->llist_info.waiting_start_idx; | |
731 | } | |
732 | } | |
733 | ||
734 | if (first_pkt_index != UNASSIGNED_INDEX) { | |
735 | pmhctl->llist_info.transmitting_start_idx = | |
736 | pmhctl->llist_info.waiting_start_idx; | |
737 | pmhctl->llist_info.transmitting_end_idx = | |
738 | pmhctl->llist_info.waiting_end_idx; | |
739 | pmhctl->llist_info.waiting_start_idx = UNASSIGNED_INDEX; | |
740 | pmhctl->llist_info.waiting_end_idx = UNASSIGNED_INDEX; | |
741 | ||
742 | /* write to the primary pointer register */ | |
743 | MDDI_MSG_DEBUG("MDDI writing primary ptr with idx=%d\n", | |
744 | first_pkt_index); | |
745 | ||
746 | pmhctl->int_type.llist_ptr_write_2++; | |
747 | ||
748 | dma_coherent_pre_ops(); | |
749 | mddi_host_reg_out(PRI_PTR, &llist_dma[first_pkt_index]); | |
750 | ||
751 | /* enable interrupt when complete */ | |
752 | mddi_host_reg_outm(INTEN, MDDI_INT_PRI_LINK_LIST_DONE, | |
753 | MDDI_INT_PRI_LINK_LIST_DONE); | |
754 | ||
755 | } | |
756 | ||
757 | } | |
758 | ||
759 | #ifndef FEATURE_MDDI_DISABLE_REVERSE | |
760 | static void mddi_read_rev_packet(byte *data_ptr) | |
761 | { | |
762 | uint16 i, length; | |
763 | mddi_host_type host_idx = mddi_curr_host; | |
764 | mddi_host_cntl_type *pmhctl = &(mhctl[host_idx]); | |
765 | ||
766 | uint8 *rev_ptr_overflow = | |
767 | (pmhctl->rev_ptr_start + MDDI_REV_BUFFER_SIZE); | |
768 | ||
769 | /* first determine the length and handle invalid lengths */ | |
770 | length = *pmhctl->rev_ptr_curr++; | |
771 | if (pmhctl->rev_ptr_curr >= rev_ptr_overflow) | |
772 | pmhctl->rev_ptr_curr = pmhctl->rev_ptr_start; | |
773 | length |= ((*pmhctl->rev_ptr_curr++) << 8); | |
774 | if (pmhctl->rev_ptr_curr >= rev_ptr_overflow) | |
775 | pmhctl->rev_ptr_curr = pmhctl->rev_ptr_start; | |
776 | if (length > (pmhctl->rev_pkt_size - 2)) { | |
777 | MDDI_MSG_ERR("Invalid rev pkt length %d\n", length); | |
778 | /* rev_pkt_size should always be <= rev_ptr_size so limit to packet size */ | |
779 | length = pmhctl->rev_pkt_size - 2; | |
780 | } | |
781 | ||
782 | /* If the data pointer is NULL, just increment the pmhctl->rev_ptr_curr. | |
783 | * Loop around if necessary. Don't bother reading the data. | |
784 | */ | |
785 | if (data_ptr == NULL) { | |
786 | pmhctl->rev_ptr_curr += length; | |
787 | if (pmhctl->rev_ptr_curr >= rev_ptr_overflow) | |
788 | pmhctl->rev_ptr_curr -= MDDI_REV_BUFFER_SIZE; | |
789 | return; | |
790 | } | |
791 | ||
792 | data_ptr[0] = length & 0x0ff; | |
793 | data_ptr[1] = length >> 8; | |
794 | data_ptr += 2; | |
795 | /* copy the data to data_ptr byte-at-a-time */ | |
796 | for (i = 0; (i < length) && (pmhctl->rev_ptr_curr < rev_ptr_overflow); | |
797 | i++) | |
798 | *data_ptr++ = *pmhctl->rev_ptr_curr++; | |
799 | if (pmhctl->rev_ptr_curr >= rev_ptr_overflow) | |
800 | pmhctl->rev_ptr_curr = pmhctl->rev_ptr_start; | |
801 | for (; (i < length) && (pmhctl->rev_ptr_curr < rev_ptr_overflow); i++) | |
802 | *data_ptr++ = *pmhctl->rev_ptr_curr++; | |
803 | } | |
804 | ||
805 | static void mddi_process_rev_packets(void) | |
806 | { | |
807 | uint32 rev_packet_count; | |
808 | word i; | |
809 | uint32 crc_errors; | |
810 | boolean mddi_reg_read_successful = FALSE; | |
811 | mddi_host_type host_idx = mddi_curr_host; | |
812 | mddi_host_cntl_type *pmhctl = &(mhctl[host_idx]); | |
813 | ||
814 | pmhctl->log_parms.rev_enc_cnt++; | |
815 | if ((pmhctl->rev_state != MDDI_REV_ENCAP_ISSUED) && | |
816 | (pmhctl->rev_state != MDDI_REV_STATUS_REQ_ISSUED) && | |
817 | (pmhctl->rev_state != MDDI_REV_CLIENT_CAP_ISSUED)) { | |
818 | MDDI_MSG_ERR("Wrong state %d for reverse int\n", | |
819 | pmhctl->rev_state); | |
820 | } | |
821 | /* Turn off MDDI_INT_REV_AVAIL interrupt */ | |
822 | mddi_host_reg_outm(INTEN, MDDI_INT_REV_DATA_AVAIL, 0); | |
823 | ||
824 | /* Clear rev data avail int */ | |
825 | mddi_host_reg_out(INT, MDDI_INT_REV_DATA_AVAIL); | |
826 | ||
827 | /* Get Number of packets */ | |
828 | rev_packet_count = mddi_host_reg_in(REV_PKT_CNT); | |
829 | ||
830 | #ifndef T_MSM7500 | |
831 | /* Clear out rev packet counter */ | |
832 | mddi_host_reg_out(REV_PKT_CNT, 0x0000); | |
833 | #endif | |
834 | ||
835 | #if defined(CONFIG_FB_MSM_MDP31) || defined(CONFIG_FB_MSM_MDP40) | |
836 | if ((pmhctl->rev_state == MDDI_REV_CLIENT_CAP_ISSUED) && | |
837 | (rev_packet_count > 0) && | |
838 | (mddi_host_core_version == 0x28 || | |
839 | mddi_host_core_version == 0x30)) { | |
840 | ||
841 | uint32 int_reg; | |
842 | uint32 max_count = 0; | |
843 | ||
844 | mddi_host_reg_out(REV_PTR, pmhctl->mddi_rev_ptr_write_val); | |
845 | int_reg = mddi_host_reg_in(INT); | |
846 | while ((int_reg & 0x100000) == 0) { | |
847 | udelay(3); | |
848 | int_reg = mddi_host_reg_in(INT); | |
849 | if (++max_count > 100) | |
850 | break; | |
851 | } | |
852 | } | |
853 | #endif | |
854 | ||
855 | /* Get CRC error count */ | |
856 | crc_errors = mddi_host_reg_in(REV_CRC_ERR); | |
857 | if (crc_errors != 0) { | |
858 | pmhctl->log_parms.rev_crc_cnt += crc_errors; | |
859 | pmhctl->stats.rev_crc_count += crc_errors; | |
860 | MDDI_MSG_ERR("!!! MDDI %d Reverse CRC Error(s) !!!\n", | |
861 | crc_errors); | |
862 | #ifndef T_MSM7500 | |
863 | /* Clear CRC error count */ | |
864 | mddi_host_reg_out(REV_CRC_ERR, 0x0000); | |
865 | #endif | |
866 | /* also issue an RTD to attempt recovery */ | |
867 | pmhctl->rtd_counter = mddi_rtd_frequency; | |
868 | } | |
869 | ||
870 | pmhctl->rtd_value = mddi_host_reg_in(RTD_VAL); | |
871 | ||
872 | MDDI_MSG_DEBUG("MDDI rev pkt cnt=%d, ptr=0x%x, RTD:0x%x\n", | |
873 | rev_packet_count, | |
874 | pmhctl->rev_ptr_curr - pmhctl->rev_ptr_start, | |
875 | pmhctl->rtd_value); | |
876 | ||
877 | if (rev_packet_count >= 1) { | |
878 | mddi_invalidate_cache_lines((uint32 *) pmhctl->rev_ptr_start, | |
879 | MDDI_REV_BUFFER_SIZE); | |
880 | } | |
881 | /* order the reads */ | |
882 | dma_coherent_post_ops(); | |
883 | for (i = 0; i < rev_packet_count; i++) { | |
884 | mddi_rev_packet_type *rev_pkt_ptr; | |
885 | ||
886 | mddi_read_rev_packet(rev_packet_data); | |
887 | ||
888 | rev_pkt_ptr = (mddi_rev_packet_type *) rev_packet_data; | |
889 | ||
890 | if (rev_pkt_ptr->packet_length > pmhctl->rev_pkt_size) { | |
891 | MDDI_MSG_ERR("!!!invalid packet size: %d\n", | |
892 | rev_pkt_ptr->packet_length); | |
893 | } | |
894 | ||
895 | MDDI_MSG_DEBUG("MDDI rev pkt 0x%x size 0x%x\n", | |
896 | rev_pkt_ptr->packet_type, | |
897 | rev_pkt_ptr->packet_length); | |
898 | ||
899 | /* Do whatever you want to do with the data based on the packet type */ | |
900 | switch (rev_pkt_ptr->packet_type) { | |
901 | case 66: /* Client Capability */ | |
902 | { | |
903 | mddi_client_capability_type | |
904 | *client_capability_pkt_ptr; | |
905 | ||
906 | client_capability_pkt_ptr = | |
907 | (mddi_client_capability_type *) | |
908 | rev_packet_data; | |
909 | MDDI_MSG_NOTICE | |
910 | ("Client Capability: Week=%d, Year=%d\n", | |
911 | client_capability_pkt_ptr-> | |
912 | Week_of_Manufacture, | |
913 | client_capability_pkt_ptr-> | |
914 | Year_of_Manufacture); | |
915 | memcpy((void *)&mddi_client_capability_pkt, | |
916 | (void *)rev_packet_data, | |
917 | sizeof(mddi_client_capability_type)); | |
918 | pmhctl->log_parms.cli_cap_cnt++; | |
919 | } | |
920 | break; | |
921 | ||
922 | case 70: /* Display Status */ | |
923 | { | |
924 | mddi_client_status_type *client_status_pkt_ptr; | |
925 | ||
926 | client_status_pkt_ptr = | |
927 | (mddi_client_status_type *) rev_packet_data; | |
928 | if ((client_status_pkt_ptr->crc_error_count != | |
929 | 0) | |
930 | || (client_status_pkt_ptr-> | |
931 | reverse_link_request != 0)) { | |
932 | MDDI_MSG_ERR | |
933 | ("Client Status: RevReq=%d, CrcErr=%d\n", | |
934 | client_status_pkt_ptr-> | |
935 | reverse_link_request, | |
936 | client_status_pkt_ptr-> | |
937 | crc_error_count); | |
938 | } else { | |
939 | MDDI_MSG_DEBUG | |
940 | ("Client Status: RevReq=%d, CrcErr=%d\n", | |
941 | client_status_pkt_ptr-> | |
942 | reverse_link_request, | |
943 | client_status_pkt_ptr-> | |
944 | crc_error_count); | |
945 | } | |
946 | pmhctl->log_parms.fwd_crc_cnt += | |
947 | client_status_pkt_ptr->crc_error_count; | |
948 | pmhctl->stats.fwd_crc_count += | |
949 | client_status_pkt_ptr->crc_error_count; | |
950 | pmhctl->log_parms.cli_stat_cnt++; | |
951 | } | |
952 | break; | |
953 | ||
954 | case 146: /* register access packet */ | |
955 | { | |
956 | mddi_register_access_packet_type | |
957 | * regacc_pkt_ptr; | |
958 | ||
959 | regacc_pkt_ptr = | |
960 | (mddi_register_access_packet_type *) | |
961 | rev_packet_data; | |
962 | ||
963 | MDDI_MSG_DEBUG | |
964 | ("Reg Acc parse reg=0x%x, value=0x%x\n", | |
965 | regacc_pkt_ptr->register_address, | |
966 | regacc_pkt_ptr->register_data_list); | |
967 | ||
968 | /* Copy register value to location passed in */ | |
969 | if (mddi_reg_read_value_ptr) { | |
970 | #if defined(T_MSM6280) && !defined(T_MSM7200) | |
971 | /* only least significant 16 bits are valid with 6280 */ | |
972 | *mddi_reg_read_value_ptr = | |
973 | regacc_pkt_ptr-> | |
974 | register_data_list & 0x0000ffff; | |
975 | #else | |
976 | *mddi_reg_read_value_ptr = | |
977 | regacc_pkt_ptr->register_data_list; | |
978 | #endif | |
979 | mddi_reg_read_successful = TRUE; | |
980 | mddi_reg_read_value_ptr = NULL; | |
981 | } | |
982 | ||
983 | #ifdef DEBUG_MDDIHOSTI | |
984 | if ((mddi_gpio.polling_enabled) && | |
985 | (regacc_pkt_ptr->register_address == | |
986 | mddi_gpio.polling_reg)) { | |
987 | /* | |
988 | * ToDo: need to call Linux GPIO call | |
989 | * here... | |
990 | */ | |
991 | mddi_client_lcd_gpio_poll( | |
992 | regacc_pkt_ptr->register_data_list); | |
993 | } | |
994 | #endif | |
995 | pmhctl->log_parms.reg_read_cnt++; | |
996 | } | |
997 | break; | |
998 | ||
999 | default: /* any other packet */ | |
1000 | { | |
1001 | uint16 hdlr; | |
1002 | ||
1003 | for (hdlr = 0; hdlr < MAX_MDDI_REV_HANDLERS; | |
1004 | hdlr++) { | |
1005 | if (mddi_rev_pkt_handler[hdlr]. | |
1006 | pkt_type == | |
1007 | rev_pkt_ptr->packet_type) { | |
1008 | (* | |
1009 | (mddi_rev_pkt_handler[hdlr]. | |
1010 | handler)) (rev_pkt_ptr); | |
1011 | /* pmhctl->rev_state = MDDI_REV_IDLE; */ | |
1012 | break; | |
1013 | } | |
1014 | } | |
1015 | if (hdlr >= MAX_MDDI_REV_HANDLERS) | |
1016 | MDDI_MSG_ERR("MDDI unknown rev pkt\n"); | |
1017 | } | |
1018 | break; | |
1019 | } | |
1020 | } | |
1021 | if ((pmhctl->rev_ptr_curr + pmhctl->rev_pkt_size) >= | |
1022 | (pmhctl->rev_ptr_start + MDDI_REV_BUFFER_SIZE)) { | |
1023 | pmhctl->rev_ptr_written = FALSE; | |
1024 | } | |
1025 | ||
1026 | if (pmhctl->rev_state == MDDI_REV_ENCAP_ISSUED) { | |
1027 | pmhctl->rev_state = MDDI_REV_IDLE; | |
1028 | if (mddi_rev_user.waiting) { | |
1029 | mddi_rev_user.waiting = FALSE; | |
1030 | complete(&(mddi_rev_user.done_comp)); | |
1031 | } else if (pmhctl->llist_info.reg_read_idx == UNASSIGNED_INDEX) { | |
1032 | MDDI_MSG_ERR | |
1033 | ("Reverse Encap state, but no reg read in progress\n"); | |
1034 | } else { | |
1035 | if ((!mddi_reg_read_successful) && | |
1036 | (mddi_reg_read_retry < mddi_reg_read_retry_max) && | |
1037 | (mddi_enable_reg_read_retry)) { | |
1038 | /* | |
1039 | * There is a race condition that can happen | |
1040 | * where the reverse encapsulation message is | |
1041 | * sent out by the MDDI host before the register | |
1042 | * read packet is sent. As a work-around for | |
1043 | * that problem we issue the reverse | |
1044 | * encapsulation one more time before giving up. | |
1045 | */ | |
1046 | if (mddi_enable_reg_read_retry_once) | |
1047 | mddi_reg_read_retry = | |
1048 | mddi_reg_read_retry_max; | |
1049 | pmhctl->rev_state = MDDI_REV_REG_READ_SENT; | |
1050 | pmhctl->stats.reg_read_failure++; | |
1051 | } else { | |
1052 | uint16 reg_read_idx = | |
1053 | pmhctl->llist_info.reg_read_idx; | |
1054 | ||
1055 | mddi_reg_read_retry = 0; | |
1056 | if (pmhctl->llist_notify[reg_read_idx].waiting) { | |
1057 | complete(& | |
1058 | (pmhctl-> | |
1059 | llist_notify[reg_read_idx]. | |
1060 | done_comp)); | |
1061 | } | |
1062 | pmhctl->llist_info.reg_read_idx = | |
1063 | UNASSIGNED_INDEX; | |
1064 | if (pmhctl->llist_notify[reg_read_idx]. | |
1065 | done_cb != NULL) { | |
1066 | (* | |
1067 | (pmhctl->llist_notify[reg_read_idx]. | |
1068 | done_cb)) (); | |
1069 | } | |
1070 | pmhctl->llist_notify[reg_read_idx].next_idx = | |
1071 | UNASSIGNED_INDEX; | |
1072 | pmhctl->llist_notify[reg_read_idx].in_use = | |
1073 | FALSE; | |
1074 | pmhctl->llist_notify[reg_read_idx].waiting = | |
1075 | FALSE; | |
1076 | pmhctl->llist_notify[reg_read_idx].done_cb = | |
1077 | NULL; | |
1078 | if (!mddi_reg_read_successful) | |
1079 | pmhctl->stats.reg_read_failure++; | |
1080 | } | |
1081 | } | |
1082 | } else if (pmhctl->rev_state == MDDI_REV_CLIENT_CAP_ISSUED) { | |
1083 | #if defined(CONFIG_FB_MSM_MDP31) || defined(CONFIG_FB_MSM_MDP40) | |
1084 | if (mddi_host_core_version == 0x28 || | |
1085 | mddi_host_core_version == 0x30) { | |
1086 | mddi_host_reg_out(FIFO_ALLOC, 0x00); | |
1087 | pmhctl->rev_ptr_written = TRUE; | |
1088 | mddi_host_reg_out(REV_PTR, | |
1089 | pmhctl->mddi_rev_ptr_write_val); | |
1090 | pmhctl->rev_ptr_curr = pmhctl->rev_ptr_start; | |
1091 | mddi_host_reg_out(CMD, 0xC00); | |
1092 | } | |
1093 | #endif | |
1094 | ||
1095 | if (mddi_rev_user.waiting) { | |
1096 | mddi_rev_user.waiting = FALSE; | |
1097 | complete(&(mddi_rev_user.done_comp)); | |
1098 | } | |
1099 | pmhctl->rev_state = MDDI_REV_IDLE; | |
1100 | } else { | |
1101 | pmhctl->rev_state = MDDI_REV_IDLE; | |
1102 | } | |
1103 | ||
1104 | /* pmhctl->rev_state = MDDI_REV_IDLE; */ | |
1105 | ||
1106 | /* Re-enable interrupt */ | |
1107 | mddi_host_reg_outm(INTEN, MDDI_INT_REV_DATA_AVAIL, | |
1108 | MDDI_INT_REV_DATA_AVAIL); | |
1109 | ||
1110 | } | |
1111 | ||
1112 | static void mddi_issue_reverse_encapsulation(void) | |
1113 | { | |
1114 | mddi_host_type host_idx = mddi_curr_host; | |
1115 | mddi_host_cntl_type *pmhctl = &(mhctl[host_idx]); | |
1116 | /* Only issue a reverse encapsulation packet if: | |
1117 | * 1) another reverse is not in progress (MDDI_REV_IDLE). | |
1118 | * 2) a register read has been sent (MDDI_REV_REG_READ_SENT). | |
1119 | * 3) forward is not in progress, because of a hw bug in client that | |
1120 | * causes forward crc errors on packet immediately after rev encap. | |
1121 | */ | |
1122 | if (((pmhctl->rev_state == MDDI_REV_IDLE) || | |
1123 | (pmhctl->rev_state == MDDI_REV_REG_READ_SENT)) && | |
1124 | (pmhctl->llist_info.transmitting_start_idx == UNASSIGNED_INDEX) && | |
1125 | (!mdp_in_processing)) { | |
1126 | uint32 mddi_command = MDDI_CMD_SEND_REV_ENCAP; | |
1127 | ||
1128 | if ((pmhctl->rev_state == MDDI_REV_REG_READ_SENT) || | |
1129 | (mddi_rev_encap_user_request == TRUE)) { | |
1130 | mddi_host_enable_io_clock(); | |
1131 | if (pmhctl->link_state == MDDI_LINK_HIBERNATING) { | |
1132 | /* need to wake up link before issuing rev encap command */ | |
1133 | MDDI_MSG_DEBUG("wake up link!\n"); | |
1134 | pmhctl->link_state = MDDI_LINK_ACTIVATING; | |
1135 | mddi_host_reg_out(CMD, MDDI_CMD_LINK_ACTIVE); | |
1136 | } else { | |
1137 | if (pmhctl->rtd_counter >= mddi_rtd_frequency) { | |
1138 | MDDI_MSG_DEBUG | |
1139 | ("mddi sending RTD command!\n"); | |
1140 | mddi_host_reg_out(CMD, | |
1141 | MDDI_CMD_SEND_RTD); | |
1142 | pmhctl->rtd_counter = 0; | |
1143 | pmhctl->log_parms.rtd_cnt++; | |
1144 | } | |
1145 | if (pmhctl->rev_state != MDDI_REV_REG_READ_SENT) { | |
1146 | /* this is generic reverse request by user, so | |
1147 | * reset the waiting flag. */ | |
1148 | mddi_rev_encap_user_request = FALSE; | |
1149 | } | |
1150 | /* link is active so send reverse encap to get register read results */ | |
1151 | pmhctl->rev_state = MDDI_REV_ENCAP_ISSUED; | |
1152 | mddi_command = MDDI_CMD_SEND_REV_ENCAP; | |
1153 | MDDI_MSG_DEBUG("sending rev encap!\n"); | |
1154 | } | |
1155 | } else | |
1156 | if ((pmhctl->client_status_cnt >= | |
1157 | mddi_client_status_frequency) | |
1158 | || mddi_client_capability_request) { | |
1159 | mddi_host_enable_io_clock(); | |
1160 | if (pmhctl->link_state == MDDI_LINK_HIBERNATING) { | |
1161 | /* only wake up the link if it client status is overdue */ | |
1162 | if ((pmhctl->client_status_cnt >= | |
1163 | (mddi_client_status_frequency * 2)) | |
1164 | || mddi_client_capability_request) { | |
1165 | /* need to wake up link before issuing rev encap command */ | |
1166 | MDDI_MSG_DEBUG("wake up link!\n"); | |
1167 | pmhctl->link_state = | |
1168 | MDDI_LINK_ACTIVATING; | |
1169 | mddi_host_reg_out(CMD, | |
1170 | MDDI_CMD_LINK_ACTIVE); | |
1171 | } | |
1172 | } else { | |
1173 | if (pmhctl->rtd_counter >= mddi_rtd_frequency) { | |
1174 | MDDI_MSG_DEBUG | |
1175 | ("mddi sending RTD command!\n"); | |
1176 | mddi_host_reg_out(CMD, | |
1177 | MDDI_CMD_SEND_RTD); | |
1178 | pmhctl->rtd_counter = 0; | |
1179 | pmhctl->log_parms.rtd_cnt++; | |
1180 | } | |
1181 | /* periodically get client status */ | |
1182 | MDDI_MSG_DEBUG | |
1183 | ("mddi sending rev enc! (get status)\n"); | |
1184 | if (mddi_client_capability_request) { | |
1185 | pmhctl->rev_state = | |
1186 | MDDI_REV_CLIENT_CAP_ISSUED; | |
1187 | mddi_command = MDDI_CMD_GET_CLIENT_CAP; | |
1188 | mddi_client_capability_request = FALSE; | |
1189 | } else { | |
1190 | pmhctl->rev_state = | |
1191 | MDDI_REV_STATUS_REQ_ISSUED; | |
1192 | pmhctl->client_status_cnt = 0; | |
1193 | mddi_command = | |
1194 | MDDI_CMD_GET_CLIENT_STATUS; | |
1195 | } | |
1196 | } | |
1197 | } | |
1198 | if ((pmhctl->rev_state == MDDI_REV_ENCAP_ISSUED) || | |
1199 | (pmhctl->rev_state == MDDI_REV_STATUS_REQ_ISSUED) || | |
1200 | (pmhctl->rev_state == MDDI_REV_CLIENT_CAP_ISSUED)) { | |
1201 | pmhctl->int_type.rev_encap_count++; | |
1202 | #if defined(T_MSM6280) && !defined(T_MSM7200) | |
1203 | mddi_rev_pointer_written = TRUE; | |
1204 | mddi_host_reg_out(REV_PTR, mddi_rev_ptr_write_val); | |
1205 | mddi_rev_ptr_curr = mddi_rev_ptr_start; | |
1206 | /* force new rev ptr command */ | |
1207 | mddi_host_reg_out(CMD, 0xC00); | |
1208 | #else | |
1209 | if (!pmhctl->rev_ptr_written) { | |
1210 | MDDI_MSG_DEBUG("writing reverse pointer!\n"); | |
1211 | pmhctl->rev_ptr_written = TRUE; | |
1212 | #if defined(CONFIG_FB_MSM_MDP31) || defined(CONFIG_FB_MSM_MDP40) | |
1213 | if ((pmhctl->rev_state == | |
1214 | MDDI_REV_CLIENT_CAP_ISSUED) && | |
1215 | (mddi_host_core_version == 0x28 || | |
1216 | mddi_host_core_version == 0x30)) { | |
1217 | pmhctl->rev_ptr_written = FALSE; | |
1218 | mddi_host_reg_out(FIFO_ALLOC, 0x02); | |
1219 | } else | |
1220 | mddi_host_reg_out(REV_PTR, | |
1221 | pmhctl-> | |
1222 | mddi_rev_ptr_write_val); | |
1223 | #else | |
1224 | mddi_host_reg_out(REV_PTR, | |
1225 | pmhctl-> | |
1226 | mddi_rev_ptr_write_val); | |
1227 | #endif | |
1228 | } | |
1229 | #endif | |
1230 | if (mddi_debug_clear_rev_data) { | |
1231 | uint16 i; | |
1232 | for (i = 0; i < MDDI_MAX_REV_DATA_SIZE / 4; i++) | |
1233 | pmhctl->rev_data_buf[i] = 0xdddddddd; | |
1234 | /* clean cache */ | |
1235 | mddi_flush_cache_lines(pmhctl->rev_data_buf, | |
1236 | MDDI_MAX_REV_DATA_SIZE); | |
1237 | } | |
1238 | ||
1239 | /* send reverse encapsulation to get needed data */ | |
1240 | mddi_host_reg_out(CMD, mddi_command); | |
1241 | } | |
1242 | } | |
1243 | ||
1244 | } | |
1245 | ||
1246 | static void mddi_process_client_initiated_wakeup(void) | |
1247 | { | |
1248 | mddi_host_type host_idx = mddi_curr_host; | |
1249 | mddi_host_cntl_type *pmhctl = &(mhctl[host_idx]); | |
1250 | ||
1251 | /* Disable MDDI_INT Interrupt, we detect client initiated wakeup one | |
1252 | * time for each entry into hibernation */ | |
1253 | mddi_host_reg_outm(INTEN, MDDI_INT_MDDI_IN, 0); | |
1254 | ||
1255 | if (host_idx == MDDI_HOST_PRIM) { | |
1256 | if (mddi_vsync_detect_enabled) { | |
1257 | mddi_host_enable_io_clock(); | |
1258 | #ifndef MDDI_HOST_DISP_LISTEN | |
1259 | /* issue command to bring up link */ | |
1260 | /* need to do this to clear the vsync condition */ | |
1261 | if (pmhctl->link_state == MDDI_LINK_HIBERNATING) { | |
1262 | pmhctl->link_state = MDDI_LINK_ACTIVATING; | |
1263 | mddi_host_reg_out(CMD, MDDI_CMD_LINK_ACTIVE); | |
1264 | } | |
1265 | #endif | |
1266 | /* | |
1267 | * Indicate to client specific code that vsync was | |
1268 | * enabled, and we did not detect a client initiated | |
1269 | * wakeup. The client specific handler can clear the | |
1270 | * condition if necessary to prevent subsequent | |
1271 | * client initiated wakeups. | |
1272 | */ | |
1273 | mddi_client_lcd_vsync_detected(TRUE); | |
1274 | pmhctl->log_parms.vsync_response_cnt++; | |
1275 | MDDI_MSG_NOTICE("MDDI_INT_IN condition\n"); | |
1276 | ||
1277 | } | |
1278 | } | |
1279 | ||
1280 | if (mddi_gpio.polling_enabled) { | |
1281 | mddi_host_enable_io_clock(); | |
1282 | /* check interrupt status now */ | |
1283 | (void)mddi_queue_register_read_int(mddi_gpio.polling_reg, | |
1284 | &mddi_gpio.polling_val); | |
1285 | } | |
1286 | } | |
1287 | #endif /* FEATURE_MDDI_DISABLE_REVERSE */ | |
1288 | ||
1289 | static void mddi_host_isr(void) | |
1290 | { | |
1291 | uint32 int_reg, int_en; | |
1292 | #ifndef FEATURE_MDDI_DISABLE_REVERSE | |
1293 | uint32 status_reg; | |
1294 | #endif | |
1295 | mddi_host_type host_idx = mddi_curr_host; | |
1296 | mddi_host_cntl_type *pmhctl = &(mhctl[host_idx]); | |
1297 | ||
1298 | if (!MDDI_HOST_IS_HCLK_ON) { | |
1299 | MDDI_HOST_ENABLE_HCLK; | |
1300 | MDDI_MSG_DEBUG("HCLK disabled, but isr is firing\n"); | |
1301 | } | |
1302 | int_reg = mddi_host_reg_in(INT); | |
1303 | int_en = mddi_host_reg_in(INTEN); | |
1304 | pmhctl->saved_int_reg = int_reg; | |
1305 | pmhctl->saved_int_en = int_en; | |
1306 | int_reg = int_reg & int_en; | |
1307 | pmhctl->int_type.count++; | |
1308 | ||
1309 | ||
1310 | #ifndef FEATURE_MDDI_DISABLE_REVERSE | |
1311 | status_reg = mddi_host_reg_in(STAT); | |
1312 | ||
1313 | if ((int_reg & MDDI_INT_MDDI_IN) || | |
1314 | ((int_en & MDDI_INT_MDDI_IN) && | |
1315 | ((int_reg == 0) || (status_reg & MDDI_STAT_CLIENT_WAKEUP_REQ)))) { | |
1316 | /* | |
1317 | * The MDDI_IN condition will clear itself, and so it is | |
1318 | * possible that MDDI_IN was the reason for the isr firing, | |
1319 | * even though the interrupt register does not have the | |
1320 | * MDDI_IN bit set. To check if this was the case we need to | |
1321 | * look at the status register bit that signifies a client | |
1322 | * initiated wakeup. If the status register bit is set, as well | |
1323 | * as the MDDI_IN interrupt enabled, then we treat this as a | |
1324 | * client initiated wakeup. | |
1325 | */ | |
1326 | if (int_reg & MDDI_INT_MDDI_IN) | |
1327 | pmhctl->int_type.in_count++; | |
1328 | mddi_process_client_initiated_wakeup(); | |
1329 | } | |
1330 | #endif | |
1331 | ||
1332 | if (int_reg & MDDI_INT_LINK_STATE_CHANGES) { | |
1333 | pmhctl->int_type.state_change_count++; | |
1334 | mddi_report_state_change(int_reg); | |
1335 | } | |
1336 | ||
1337 | if (int_reg & MDDI_INT_PRI_LINK_LIST_DONE) { | |
1338 | pmhctl->int_type.ll_done_count++; | |
1339 | mddi_process_link_list_done(); | |
1340 | } | |
1341 | #ifndef FEATURE_MDDI_DISABLE_REVERSE | |
1342 | if (int_reg & MDDI_INT_REV_DATA_AVAIL) { | |
1343 | pmhctl->int_type.rev_avail_count++; | |
1344 | mddi_process_rev_packets(); | |
1345 | } | |
1346 | #endif | |
1347 | ||
1348 | if (int_reg & MDDI_INT_ERROR_CONDITIONS) { | |
1349 | pmhctl->int_type.error_count++; | |
1350 | mddi_report_errors(int_reg); | |
1351 | ||
1352 | mddi_host_reg_out(INT, int_reg & MDDI_INT_ERROR_CONDITIONS); | |
1353 | } | |
1354 | #ifndef FEATURE_MDDI_DISABLE_REVERSE | |
1355 | mddi_issue_reverse_encapsulation(); | |
1356 | ||
1357 | if ((pmhctl->rev_state != MDDI_REV_ENCAP_ISSUED) && | |
1358 | (pmhctl->rev_state != MDDI_REV_STATUS_REQ_ISSUED)) | |
1359 | #endif | |
1360 | /* don't want simultaneous reverse and forward with Eagle */ | |
1361 | mddi_queue_forward_linked_list(); | |
1362 | ||
1363 | if (int_reg & MDDI_INT_NO_CMD_PKTS_PEND) { | |
1364 | /* this interrupt is used to kick the isr when hibernation is disabled */ | |
1365 | mddi_host_reg_outm(INTEN, MDDI_INT_NO_CMD_PKTS_PEND, 0); | |
1366 | } | |
1367 | ||
1368 | if ((!mddi_host_mdp_active_flag) && | |
1369 | (!mddi_vsync_detect_enabled) && | |
1370 | (pmhctl->llist_info.transmitting_start_idx == UNASSIGNED_INDEX) && | |
1371 | (pmhctl->llist_info.waiting_start_idx == UNASSIGNED_INDEX) && | |
1372 | (pmhctl->rev_state == MDDI_REV_IDLE)) { | |
1373 | if (pmhctl->link_state == MDDI_LINK_HIBERNATING) { | |
1374 | mddi_host_disable_io_clock(); | |
1375 | mddi_host_disable_hclk(); | |
1376 | } | |
1377 | #ifdef FEATURE_MDDI_HOST_ENABLE_EARLY_HIBERNATION | |
1378 | else if ((pmhctl->link_state == MDDI_LINK_ACTIVE) && | |
1379 | (!pmhctl->disable_hibernation)) { | |
1380 | mddi_host_reg_out(CMD, MDDI_CMD_POWERDOWN); | |
1381 | } | |
1382 | #endif | |
1383 | } | |
1384 | } | |
1385 | ||
1386 | static void mddi_host_isr_primary(void) | |
1387 | { | |
1388 | mddi_curr_host = MDDI_HOST_PRIM; | |
1389 | mddi_host_isr(); | |
1390 | } | |
1391 | ||
1392 | irqreturn_t mddi_pmdh_isr_proxy(int irq, void *ptr) | |
1393 | { | |
1394 | mddi_host_isr_primary(); | |
1395 | return IRQ_HANDLED; | |
1396 | } | |
1397 | ||
1398 | static void mddi_host_isr_external(void) | |
1399 | { | |
1400 | mddi_curr_host = MDDI_HOST_EXT; | |
1401 | mddi_host_isr(); | |
1402 | mddi_curr_host = MDDI_HOST_PRIM; | |
1403 | } | |
1404 | ||
1405 | irqreturn_t mddi_emdh_isr_proxy(int irq, void *ptr) | |
1406 | { | |
1407 | mddi_host_isr_external(); | |
1408 | return IRQ_HANDLED; | |
1409 | } | |
1410 | ||
1411 | static void mddi_host_initialize_registers(mddi_host_type host_idx) | |
1412 | { | |
1413 | uint32 pad_reg_val; | |
1414 | mddi_host_cntl_type *pmhctl = &(mhctl[host_idx]); | |
1415 | ||
1416 | if (pmhctl->driver_state == MDDI_DRIVER_ENABLED) | |
1417 | return; | |
1418 | ||
1419 | /* turn on HCLK to MDDI host core */ | |
1420 | mddi_host_enable_hclk(); | |
1421 | ||
1422 | /* MDDI Reset command */ | |
1423 | mddi_host_reg_out(CMD, MDDI_CMD_RESET); | |
1424 | ||
1425 | /* Version register (= 0x01) */ | |
1426 | mddi_host_reg_out(VERSION, 0x0001); | |
1427 | ||
1428 | /* Bytes per subframe register */ | |
1429 | mddi_host_reg_out(BPS, MDDI_HOST_BYTES_PER_SUBFRAME); | |
1430 | ||
1431 | /* Subframes per media frames register (= 0x03) */ | |
1432 | mddi_host_reg_out(SPM, 0x0003); | |
1433 | ||
1434 | /* Turn Around 1 register (= 0x05) */ | |
1435 | mddi_host_reg_out(TA1_LEN, 0x0005); | |
1436 | ||
1437 | /* Turn Around 2 register (= 0x0C) */ | |
1438 | mddi_host_reg_out(TA2_LEN, MDDI_HOST_TA2_LEN); | |
1439 | ||
1440 | /* Drive hi register (= 0x96) */ | |
1441 | mddi_host_reg_out(DRIVE_HI, 0x0096); | |
1442 | ||
1443 | /* Drive lo register (= 0x32) */ | |
1444 | mddi_host_reg_out(DRIVE_LO, 0x0032); | |
1445 | ||
1446 | /* Display wakeup count register (= 0x3c) */ | |
1447 | mddi_host_reg_out(DISP_WAKE, 0x003c); | |
1448 | ||
1449 | /* Reverse Rate Divisor register (= 0x2) */ | |
1450 | mddi_host_reg_out(REV_RATE_DIV, MDDI_HOST_REV_RATE_DIV); | |
1451 | ||
1452 | #ifndef FEATURE_MDDI_DISABLE_REVERSE | |
1453 | /* Reverse Pointer Size */ | |
1454 | mddi_host_reg_out(REV_SIZE, MDDI_REV_BUFFER_SIZE); | |
1455 | ||
1456 | /* Rev Encap Size */ | |
1457 | mddi_host_reg_out(REV_ENCAP_SZ, pmhctl->rev_pkt_size); | |
1458 | #endif | |
1459 | ||
1460 | /* Periodic Rev Encap */ | |
1461 | /* don't send periodically */ | |
1462 | mddi_host_reg_out(CMD, MDDI_CMD_PERIODIC_REV_ENCAP); | |
1463 | ||
1464 | pad_reg_val = mddi_host_reg_in(PAD_CTL); | |
1465 | if (pad_reg_val == 0) { | |
1466 | /* If we are turning on band gap, need to wait 5us before turning | |
1467 | * on the rest of the PAD */ | |
1468 | mddi_host_reg_out(PAD_CTL, 0x08000); | |
1469 | udelay(5); | |
1470 | } | |
1471 | #ifdef T_MSM7200 | |
1472 | /* Recommendation from PAD hw team */ | |
1473 | mddi_host_reg_out(PAD_CTL, 0xa850a); | |
1474 | #else | |
1475 | /* Recommendation from PAD hw team */ | |
1476 | mddi_host_reg_out(PAD_CTL, 0xa850f); | |
1477 | #endif | |
1478 | ||
1479 | #if defined(CONFIG_FB_MSM_MDP31) || defined(CONFIG_FB_MSM_MDP40) | |
1480 | mddi_host_reg_out(PAD_IO_CTL, 0x00320000); | |
1481 | mddi_host_reg_out(PAD_CAL, 0x00220020); | |
1482 | #endif | |
1483 | ||
1484 | mddi_host_core_version = mddi_host_reg_inm(CORE_VER, 0xffff); | |
1485 | ||
1486 | #ifndef FEATURE_MDDI_DISABLE_REVERSE | |
1487 | if (mddi_host_core_version >= 8) | |
1488 | mddi_rev_ptr_workaround = FALSE; | |
1489 | pmhctl->rev_ptr_curr = pmhctl->rev_ptr_start; | |
1490 | #endif | |
1491 | ||
1492 | if ((mddi_host_core_version > 8) && (mddi_host_core_version < 0x19)) | |
1493 | mddi_host_reg_out(TEST, 0x2); | |
1494 | ||
1495 | /* Need an even number for counts */ | |
1496 | mddi_host_reg_out(DRIVER_START_CNT, 0x60006); | |
1497 | ||
1498 | #ifndef T_MSM7500 | |
1499 | /* Setup defaults for MDP related register */ | |
1500 | mddi_host_reg_out(MDP_VID_FMT_DES, 0x5666); | |
1501 | mddi_host_reg_out(MDP_VID_PIX_ATTR, 0x00C3); | |
1502 | mddi_host_reg_out(MDP_VID_CLIENTID, 0); | |
1503 | #endif | |
1504 | ||
1505 | /* automatically hibernate after 1 empty subframe */ | |
1506 | if (pmhctl->disable_hibernation) | |
1507 | mddi_host_reg_out(CMD, MDDI_CMD_HIBERNATE); | |
1508 | else | |
1509 | mddi_host_reg_out(CMD, MDDI_CMD_HIBERNATE | 1); | |
1510 | ||
1511 | /* Bring up link if display (client) requests it */ | |
1512 | #ifdef MDDI_HOST_DISP_LISTEN | |
1513 | mddi_host_reg_out(CMD, MDDI_CMD_DISP_LISTEN); | |
1514 | #else | |
1515 | mddi_host_reg_out(CMD, MDDI_CMD_DISP_IGNORE); | |
1516 | #endif | |
1517 | ||
1518 | } | |
1519 | ||
1520 | void mddi_host_configure_interrupts(mddi_host_type host_idx, boolean enable) | |
1521 | { | |
1522 | unsigned long flags; | |
1523 | mddi_host_cntl_type *pmhctl = &(mhctl[host_idx]); | |
1524 | ||
1525 | spin_lock_irqsave(&mddi_host_spin_lock, flags); | |
1526 | ||
1527 | /* turn on HCLK to MDDI host core if it has been disabled */ | |
1528 | mddi_host_enable_hclk(); | |
1529 | /* Clear MDDI Interrupt enable reg */ | |
1530 | mddi_host_reg_out(INTEN, 0); | |
1531 | ||
1532 | spin_unlock_irqrestore(&mddi_host_spin_lock, flags); | |
1533 | ||
1534 | if (enable) { | |
1535 | pmhctl->driver_state = MDDI_DRIVER_ENABLED; | |
1536 | ||
1537 | if (host_idx == MDDI_HOST_PRIM) { | |
1538 | if (request_irq | |
1539 | (INT_MDDI_PRI, mddi_pmdh_isr_proxy, IRQF_DISABLED, | |
1540 | "PMDH", 0) != 0) | |
1541 | printk(KERN_ERR | |
1542 | "a mddi: unable to request_irq\n"); | |
1543 | else | |
1544 | int_mddi_pri_flag = TRUE; | |
1545 | } else { | |
1546 | if (request_irq | |
1547 | (INT_MDDI_EXT, mddi_emdh_isr_proxy, IRQF_DISABLED, | |
1548 | "EMDH", 0) != 0) | |
1549 | printk(KERN_ERR | |
1550 | "b mddi: unable to request_irq\n"); | |
1551 | else | |
1552 | int_mddi_ext_flag = TRUE; | |
1553 | } | |
1554 | ||
1555 | /* Set MDDI Interrupt enable reg -- Enable Reverse data avail */ | |
1556 | #ifdef FEATURE_MDDI_DISABLE_REVERSE | |
1557 | mddi_host_reg_out(INTEN, | |
1558 | MDDI_INT_ERROR_CONDITIONS | | |
1559 | MDDI_INT_LINK_STATE_CHANGES); | |
1560 | #else | |
1561 | /* Reverse Pointer register */ | |
1562 | pmhctl->rev_ptr_written = FALSE; | |
1563 | ||
1564 | mddi_host_reg_out(INTEN, | |
1565 | MDDI_INT_REV_DATA_AVAIL | | |
1566 | MDDI_INT_ERROR_CONDITIONS | | |
1567 | MDDI_INT_LINK_STATE_CHANGES); | |
1568 | pmhctl->rtd_counter = mddi_rtd_frequency; | |
1569 | pmhctl->client_status_cnt = 0; | |
1570 | #endif | |
1571 | } else { | |
1572 | if (pmhctl->driver_state == MDDI_DRIVER_ENABLED) | |
1573 | pmhctl->driver_state = MDDI_DRIVER_DISABLED; | |
1574 | } | |
1575 | ||
1576 | } | |
1577 | ||
1578 | static void mddi_host_powerup(mddi_host_type host_idx) | |
1579 | { | |
1580 | mddi_host_cntl_type *pmhctl = &(mhctl[host_idx]); | |
1581 | ||
1582 | if (pmhctl->link_state != MDDI_LINK_DISABLED) | |
1583 | return; | |
1584 | ||
1585 | /* enable IO_CLK and hclk to MDDI host core */ | |
1586 | mddi_host_enable_io_clock(); | |
1587 | ||
1588 | mddi_host_initialize_registers(host_idx); | |
1589 | mddi_host_configure_interrupts(host_idx, TRUE); | |
1590 | ||
1591 | pmhctl->link_state = MDDI_LINK_ACTIVATING; | |
1592 | ||
1593 | /* Link activate command */ | |
1594 | mddi_host_reg_out(CMD, MDDI_CMD_LINK_ACTIVE); | |
1595 | ||
1596 | #ifdef CLKRGM_MDDI_IO_CLOCK_IN_MHZ | |
1597 | MDDI_MSG_NOTICE("MDDI Host: Activating Link %d Mbps\n", | |
1598 | CLKRGM_MDDI_IO_CLOCK_IN_MHZ * 2); | |
1599 | #else | |
1600 | MDDI_MSG_NOTICE("MDDI Host: Activating Link\n"); | |
1601 | #endif | |
1602 | ||
1603 | /* Initialize the timer */ | |
1604 | if (host_idx == MDDI_HOST_PRIM) | |
1605 | mddi_host_timer_service(0); | |
1606 | } | |
1607 | ||
1608 | void mddi_host_init(mddi_host_type host_idx) | |
1609 | /* Write out the MDDI configuration registers */ | |
1610 | { | |
1611 | static boolean initialized = FALSE; | |
1612 | mddi_host_cntl_type *pmhctl; | |
1613 | ||
1614 | if (host_idx >= MDDI_NUM_HOST_CORES) { | |
1615 | MDDI_MSG_ERR("Invalid host core index\n"); | |
1616 | return; | |
1617 | } | |
1618 | ||
1619 | if (!initialized) { | |
1620 | uint16 idx; | |
1621 | mddi_host_type host; | |
1622 | for (host = MDDI_HOST_PRIM; host < MDDI_NUM_HOST_CORES; host++) { | |
1623 | pmhctl = &(mhctl[host]); | |
1624 | initialized = TRUE; | |
1625 | ||
1626 | pmhctl->llist_ptr = | |
1627 | dma_alloc_coherent(NULL, MDDI_LLIST_POOL_SIZE, | |
1628 | &(pmhctl->llist_dma_addr), | |
1629 | GFP_KERNEL); | |
1630 | pmhctl->llist_dma_ptr = | |
1631 | (mddi_linked_list_type *) (void *)pmhctl-> | |
1632 | llist_dma_addr; | |
1633 | #ifdef FEATURE_MDDI_DISABLE_REVERSE | |
1634 | pmhctl->rev_data_buf = NULL; | |
1635 | if (pmhctl->llist_ptr == NULL) | |
1636 | #else | |
1637 | mddi_rev_user.waiting = FALSE; | |
1638 | init_completion(&(mddi_rev_user.done_comp)); | |
1639 | pmhctl->rev_data_buf = | |
1640 | dma_alloc_coherent(NULL, MDDI_MAX_REV_DATA_SIZE, | |
1641 | &(pmhctl->rev_data_dma_addr), | |
1642 | GFP_KERNEL); | |
1643 | if ((pmhctl->llist_ptr == NULL) | |
1644 | || (pmhctl->rev_data_buf == NULL)) | |
1645 | #endif | |
1646 | { | |
1647 | MDDI_MSG_CRIT | |
1648 | ("unable to alloc non-cached memory\n"); | |
1649 | } | |
1650 | llist_extern[host] = pmhctl->llist_ptr; | |
1651 | llist_dma_extern[host] = pmhctl->llist_dma_ptr; | |
1652 | llist_extern_notify[host] = pmhctl->llist_notify; | |
1653 | ||
1654 | for (idx = 0; idx < UNASSIGNED_INDEX; idx++) { | |
1655 | init_completion(& | |
1656 | (pmhctl->llist_notify[idx]. | |
1657 | done_comp)); | |
1658 | } | |
1659 | init_completion(&(pmhctl->mddi_llist_avail_comp)); | |
1660 | spin_lock_init(&mddi_host_spin_lock); | |
1661 | pmhctl->mddi_waiting_for_llist_avail = FALSE; | |
1662 | pmhctl->mddi_rev_ptr_write_val = | |
1663 | (uint32) (void *)(pmhctl->rev_data_dma_addr); | |
1664 | pmhctl->rev_ptr_start = (void *)pmhctl->rev_data_buf; | |
1665 | ||
1666 | pmhctl->rev_pkt_size = MDDI_DEFAULT_REV_PKT_SIZE; | |
1667 | pmhctl->rev_state = MDDI_REV_IDLE; | |
1668 | #ifdef IMAGE_MODEM_PROC | |
1669 | /* assume hibernation state is last state from APPS proc, so that | |
1670 | * we don't reinitialize the host core */ | |
1671 | pmhctl->link_state = MDDI_LINK_HIBERNATING; | |
1672 | #else | |
1673 | pmhctl->link_state = MDDI_LINK_DISABLED; | |
1674 | #endif | |
1675 | pmhctl->driver_state = MDDI_DRIVER_DISABLED; | |
1676 | pmhctl->disable_hibernation = FALSE; | |
1677 | ||
1678 | /* initialize llist variables */ | |
1679 | pmhctl->llist_info.transmitting_start_idx = | |
1680 | UNASSIGNED_INDEX; | |
1681 | pmhctl->llist_info.transmitting_end_idx = | |
1682 | UNASSIGNED_INDEX; | |
1683 | pmhctl->llist_info.waiting_start_idx = UNASSIGNED_INDEX; | |
1684 | pmhctl->llist_info.waiting_end_idx = UNASSIGNED_INDEX; | |
1685 | pmhctl->llist_info.reg_read_idx = UNASSIGNED_INDEX; | |
1686 | pmhctl->llist_info.next_free_idx = | |
1687 | MDDI_FIRST_DYNAMIC_LLIST_IDX; | |
1688 | pmhctl->llist_info.reg_read_waiting = FALSE; | |
1689 | ||
1690 | mddi_vsync_detect_enabled = FALSE; | |
1691 | mddi_gpio.polling_enabled = FALSE; | |
1692 | ||
1693 | pmhctl->int_type.count = 0; | |
1694 | pmhctl->int_type.in_count = 0; | |
1695 | pmhctl->int_type.disp_req_count = 0; | |
1696 | pmhctl->int_type.state_change_count = 0; | |
1697 | pmhctl->int_type.ll_done_count = 0; | |
1698 | pmhctl->int_type.rev_avail_count = 0; | |
1699 | pmhctl->int_type.error_count = 0; | |
1700 | pmhctl->int_type.rev_encap_count = 0; | |
1701 | pmhctl->int_type.llist_ptr_write_1 = 0; | |
1702 | pmhctl->int_type.llist_ptr_write_2 = 0; | |
1703 | ||
1704 | pmhctl->stats.fwd_crc_count = 0; | |
1705 | pmhctl->stats.rev_crc_count = 0; | |
1706 | pmhctl->stats.pri_underflow = 0; | |
1707 | pmhctl->stats.sec_underflow = 0; | |
1708 | pmhctl->stats.rev_overflow = 0; | |
1709 | pmhctl->stats.pri_overwrite = 0; | |
1710 | pmhctl->stats.sec_overwrite = 0; | |
1711 | pmhctl->stats.rev_overwrite = 0; | |
1712 | pmhctl->stats.dma_failure = 0; | |
1713 | pmhctl->stats.rtd_failure = 0; | |
1714 | pmhctl->stats.reg_read_failure = 0; | |
1715 | #ifdef FEATURE_MDDI_UNDERRUN_RECOVERY | |
1716 | pmhctl->stats.pri_underrun_detected = 0; | |
1717 | #endif | |
1718 | ||
1719 | pmhctl->log_parms.rtd_cnt = 0; | |
1720 | pmhctl->log_parms.rev_enc_cnt = 0; | |
1721 | pmhctl->log_parms.vid_cnt = 0; | |
1722 | pmhctl->log_parms.reg_acc_cnt = 0; | |
1723 | pmhctl->log_parms.cli_stat_cnt = 0; | |
1724 | pmhctl->log_parms.cli_cap_cnt = 0; | |
1725 | pmhctl->log_parms.reg_read_cnt = 0; | |
1726 | pmhctl->log_parms.link_active_cnt = 0; | |
1727 | pmhctl->log_parms.link_hibernate_cnt = 0; | |
1728 | pmhctl->log_parms.fwd_crc_cnt = 0; | |
1729 | pmhctl->log_parms.rev_crc_cnt = 0; | |
1730 | pmhctl->log_parms.vsync_response_cnt = 0; | |
1731 | ||
1732 | prev_parms[host_idx] = pmhctl->log_parms; | |
1733 | mddi_client_capability_pkt.packet_length = 0; | |
1734 | } | |
1735 | ||
1736 | #ifndef T_MSM7500 | |
1737 | /* tell clock driver we are user of this PLL */ | |
1738 | MDDI_HOST_ENABLE_IO_CLOCK; | |
1739 | #endif | |
1740 | } | |
1741 | ||
1742 | mddi_host_powerup(host_idx); | |
1743 | pmhctl = &(mhctl[host_idx]); | |
1744 | } | |
1745 | ||
1746 | #ifdef CONFIG_FB_MSM_MDDI_AUTO_DETECT | |
1747 | static uint32 mddi_client_id; | |
1748 | ||
1749 | uint32 mddi_get_client_id(void) | |
1750 | { | |
1751 | ||
1752 | #ifndef FEATURE_MDDI_DISABLE_REVERSE | |
1753 | mddi_host_type host_idx = MDDI_HOST_PRIM; | |
1754 | static boolean client_detection_try = FALSE; | |
1755 | mddi_host_cntl_type *pmhctl; | |
1756 | unsigned long flags; | |
1757 | uint16 saved_rev_pkt_size; | |
1758 | ||
1759 | if (!client_detection_try) { | |
1760 | /* Toshiba display requires larger drive_lo value */ | |
1761 | mddi_host_reg_out(DRIVE_LO, 0x0050); | |
1762 | ||
1763 | pmhctl = &(mhctl[MDDI_HOST_PRIM]); | |
1764 | ||
1765 | saved_rev_pkt_size = pmhctl->rev_pkt_size; | |
1766 | ||
1767 | /* Increase Rev Encap Size */ | |
1768 | pmhctl->rev_pkt_size = MDDI_CLIENT_CAPABILITY_REV_PKT_SIZE; | |
1769 | mddi_host_reg_out(REV_ENCAP_SZ, pmhctl->rev_pkt_size); | |
1770 | ||
1771 | /* disable hibernation temporarily */ | |
1772 | if (!pmhctl->disable_hibernation) | |
1773 | mddi_host_reg_out(CMD, MDDI_CMD_HIBERNATE); | |
1774 | ||
1775 | mddi_rev_user.waiting = TRUE; | |
1776 | INIT_COMPLETION(mddi_rev_user.done_comp); | |
1777 | ||
1778 | spin_lock_irqsave(&mddi_host_spin_lock, flags); | |
1779 | ||
1780 | /* turn on clock(s), if they have been disabled */ | |
1781 | mddi_host_enable_hclk(); | |
1782 | mddi_host_enable_io_clock(); | |
1783 | ||
1784 | mddi_client_capability_request = TRUE; | |
1785 | ||
1786 | if (pmhctl->rev_state == MDDI_REV_IDLE) { | |
1787 | /* attempt to send the reverse encapsulation now */ | |
1788 | mddi_issue_reverse_encapsulation(); | |
1789 | } | |
1790 | spin_unlock_irqrestore(&mddi_host_spin_lock, flags); | |
1791 | ||
1792 | wait_for_completion_killable(&(mddi_rev_user.done_comp)); | |
1793 | ||
1794 | /* Set Rev Encap Size back to its original value */ | |
1795 | pmhctl->rev_pkt_size = saved_rev_pkt_size; | |
1796 | mddi_host_reg_out(REV_ENCAP_SZ, pmhctl->rev_pkt_size); | |
1797 | ||
1798 | /* reenable auto-hibernate */ | |
1799 | if (!pmhctl->disable_hibernation) | |
1800 | mddi_host_reg_out(CMD, MDDI_CMD_HIBERNATE | 1); | |
1801 | ||
1802 | mddi_host_reg_out(DRIVE_LO, 0x0032); | |
1803 | client_detection_try = TRUE; | |
1804 | ||
1805 | mddi_client_id = (mddi_client_capability_pkt.Mfr_Name<<16) | | |
1806 | mddi_client_capability_pkt.Product_Code; | |
1807 | ||
1808 | if (!mddi_client_id) | |
1809 | mddi_disable(1); | |
1810 | } | |
1811 | ||
1812 | #if 0 | |
1813 | switch (mddi_client_capability_pkt.Mfr_Name) { | |
1814 | case 0x4474: | |
1815 | if ((mddi_client_capability_pkt.Product_Code != 0x8960) && | |
1816 | (target == DISPLAY_1)) { | |
1817 | ret = PRISM_WVGA; | |
1818 | } | |
1819 | break; | |
1820 | ||
1821 | case 0xD263: | |
1822 | if (target == DISPLAY_1) | |
1823 | ret = TOSHIBA_VGA_PRIM; | |
1824 | else if (target == DISPLAY_2) | |
1825 | ret = TOSHIBA_QCIF_SECD; | |
1826 | break; | |
1827 | ||
1828 | case 0: | |
1829 | if (mddi_client_capability_pkt.Product_Code == 0x8835) { | |
1830 | if (target == DISPLAY_1) | |
1831 | ret = SHARP_QVGA_PRIM; | |
1832 | else if (target == DISPLAY_2) | |
1833 | ret = SHARP_128x128_SECD; | |
1834 | } | |
1835 | break; | |
1836 | ||
1837 | default: | |
1838 | break; | |
1839 | } | |
1840 | ||
1841 | if ((!client_detection_try) && (ret != TOSHIBA_VGA_PRIM) | |
1842 | && (ret != TOSHIBA_QCIF_SECD)) { | |
1843 | /* Not a Toshiba display, so change drive_lo back to default value */ | |
1844 | mddi_host_reg_out(DRIVE_LO, 0x0032); | |
1845 | } | |
1846 | #endif | |
1847 | ||
1848 | #endif | |
1849 | ||
1850 | return mddi_client_id; | |
1851 | } | |
1852 | #endif | |
1853 | ||
1854 | void mddi_host_powerdown(mddi_host_type host_idx) | |
1855 | { | |
1856 | mddi_host_cntl_type *pmhctl = &(mhctl[host_idx]); | |
1857 | ||
1858 | if (host_idx >= MDDI_NUM_HOST_CORES) { | |
1859 | MDDI_MSG_ERR("Invalid host core index\n"); | |
1860 | return; | |
1861 | } | |
1862 | ||
1863 | if (pmhctl->driver_state == MDDI_DRIVER_RESET) { | |
1864 | return; | |
1865 | } | |
1866 | ||
1867 | if (host_idx == MDDI_HOST_PRIM) { | |
1868 | /* disable timer */ | |
1869 | del_timer(&mddi_host_timer); | |
1870 | } | |
1871 | ||
1872 | mddi_host_configure_interrupts(host_idx, FALSE); | |
1873 | ||
1874 | /* turn on HCLK to MDDI host core if it has been disabled */ | |
1875 | mddi_host_enable_hclk(); | |
1876 | ||
1877 | /* MDDI Reset command */ | |
1878 | mddi_host_reg_out(CMD, MDDI_CMD_RESET); | |
1879 | ||
1880 | /* Pad Control Register */ | |
1881 | mddi_host_reg_out(PAD_CTL, 0x0); | |
1882 | ||
1883 | /* disable IO_CLK and hclk to MDDI host core */ | |
1884 | mddi_host_disable_io_clock(); | |
1885 | mddi_host_disable_hclk(); | |
1886 | ||
1887 | pmhctl->link_state = MDDI_LINK_DISABLED; | |
1888 | pmhctl->driver_state = MDDI_DRIVER_RESET; | |
1889 | ||
1890 | MDDI_MSG_NOTICE("MDDI Host: Disabling Link\n"); | |
1891 | ||
1892 | } | |
1893 | ||
1894 | uint16 mddi_get_next_free_llist_item(mddi_host_type host_idx, boolean wait) | |
1895 | { | |
1896 | unsigned long flags; | |
1897 | uint16 ret_idx; | |
1898 | boolean forced_wait = FALSE; | |
1899 | mddi_host_cntl_type *pmhctl = &(mhctl[host_idx]); | |
1900 | ||
1901 | ret_idx = pmhctl->llist_info.next_free_idx; | |
1902 | ||
1903 | pmhctl->llist_info.next_free_idx++; | |
1904 | if (pmhctl->llist_info.next_free_idx >= MDDI_NUM_DYNAMIC_LLIST_ITEMS) | |
1905 | pmhctl->llist_info.next_free_idx = MDDI_FIRST_DYNAMIC_LLIST_IDX; | |
1906 | spin_lock_irqsave(&mddi_host_spin_lock, flags); | |
1907 | if (pmhctl->llist_notify[ret_idx].in_use) { | |
1908 | if (!wait) { | |
1909 | pmhctl->llist_info.next_free_idx = ret_idx; | |
1910 | ret_idx = UNASSIGNED_INDEX; | |
1911 | } else { | |
1912 | forced_wait = TRUE; | |
1913 | INIT_COMPLETION(pmhctl->mddi_llist_avail_comp); | |
1914 | } | |
1915 | } | |
1916 | spin_unlock_irqrestore(&mddi_host_spin_lock, flags); | |
1917 | ||
1918 | if (forced_wait) { | |
1919 | wait_for_completion_killable(& | |
1920 | (pmhctl-> | |
1921 | mddi_llist_avail_comp)); | |
1922 | MDDI_MSG_ERR("task waiting on mddi llist item\n"); | |
1923 | } | |
1924 | ||
1925 | if (ret_idx != UNASSIGNED_INDEX) { | |
1926 | pmhctl->llist_notify[ret_idx].waiting = FALSE; | |
1927 | pmhctl->llist_notify[ret_idx].done_cb = NULL; | |
1928 | pmhctl->llist_notify[ret_idx].in_use = TRUE; | |
1929 | pmhctl->llist_notify[ret_idx].next_idx = UNASSIGNED_INDEX; | |
1930 | } | |
1931 | ||
1932 | return ret_idx; | |
1933 | } | |
1934 | ||
1935 | uint16 mddi_get_reg_read_llist_item(mddi_host_type host_idx, boolean wait) | |
1936 | { | |
1937 | #ifdef FEATURE_MDDI_DISABLE_REVERSE | |
1938 | MDDI_MSG_CRIT("No reverse link available\n"); | |
1939 | (void)wait; | |
1940 | return FALSE; | |
1941 | #else | |
1942 | unsigned long flags; | |
1943 | uint16 ret_idx; | |
1944 | boolean error = FALSE; | |
1945 | mddi_host_cntl_type *pmhctl = &(mhctl[host_idx]); | |
1946 | ||
1947 | spin_lock_irqsave(&mddi_host_spin_lock, flags); | |
1948 | if (pmhctl->llist_info.reg_read_idx != UNASSIGNED_INDEX) { | |
1949 | /* need to block here or is this an error condition? */ | |
1950 | error = TRUE; | |
1951 | ret_idx = UNASSIGNED_INDEX; | |
1952 | } | |
1953 | spin_unlock_irqrestore(&mddi_host_spin_lock, flags); | |
1954 | ||
1955 | if (!error) { | |
1956 | ret_idx = pmhctl->llist_info.reg_read_idx = | |
1957 | mddi_get_next_free_llist_item(host_idx, wait); | |
1958 | /* clear the reg_read_waiting flag */ | |
1959 | pmhctl->llist_info.reg_read_waiting = FALSE; | |
1960 | } | |
1961 | ||
1962 | if (error) | |
1963 | MDDI_MSG_ERR("***** Reg read still in progress! ****\n"); | |
1964 | return ret_idx; | |
1965 | #endif | |
1966 | ||
1967 | } | |
1968 | ||
1969 | void mddi_queue_forward_packets(uint16 first_llist_idx, | |
1970 | uint16 last_llist_idx, | |
1971 | boolean wait, | |
1972 | mddi_llist_done_cb_type llist_done_cb, | |
1973 | mddi_host_type host_idx) | |
1974 | { | |
1975 | unsigned long flags; | |
1976 | mddi_linked_list_type *llist; | |
1977 | mddi_linked_list_type *llist_dma; | |
1978 | mddi_host_cntl_type *pmhctl = &(mhctl[host_idx]); | |
1979 | ||
1980 | if ((first_llist_idx >= UNASSIGNED_INDEX) || | |
1981 | (last_llist_idx >= UNASSIGNED_INDEX)) { | |
1982 | MDDI_MSG_ERR("MDDI queueing invalid linked list\n"); | |
1983 | return; | |
1984 | } | |
1985 | ||
1986 | if (pmhctl->link_state == MDDI_LINK_DISABLED) | |
1987 | MDDI_MSG_CRIT("MDDI host powered down!\n"); | |
1988 | ||
1989 | llist = pmhctl->llist_ptr; | |
1990 | llist_dma = pmhctl->llist_dma_ptr; | |
1991 | ||
1992 | /* clean cache so MDDI host can read data */ | |
1993 | memory_barrier(); | |
1994 | ||
1995 | pmhctl->llist_notify[last_llist_idx].waiting = wait; | |
1996 | if (wait) | |
1997 | INIT_COMPLETION(pmhctl->llist_notify[last_llist_idx].done_comp); | |
1998 | pmhctl->llist_notify[last_llist_idx].done_cb = llist_done_cb; | |
1999 | ||
2000 | spin_lock_irqsave(&mddi_host_spin_lock, flags); | |
2001 | ||
2002 | if ((pmhctl->llist_info.transmitting_start_idx == UNASSIGNED_INDEX) && | |
2003 | (pmhctl->llist_info.waiting_start_idx == UNASSIGNED_INDEX) && | |
2004 | (pmhctl->rev_state == MDDI_REV_IDLE)) { | |
2005 | /* no packets are currently transmitting */ | |
2006 | #ifndef FEATURE_MDDI_DISABLE_REVERSE | |
2007 | if (first_llist_idx == pmhctl->llist_info.reg_read_idx) { | |
2008 | /* This is the special case where the packet is a register read. */ | |
2009 | pmhctl->rev_state = MDDI_REV_REG_READ_ISSUED; | |
2010 | mddi_reg_read_retry = 0; | |
2011 | /* mddi_rev_reg_read_attempt = 1; */ | |
2012 | } | |
2013 | #endif | |
2014 | /* assign transmitting index values */ | |
2015 | pmhctl->llist_info.transmitting_start_idx = first_llist_idx; | |
2016 | pmhctl->llist_info.transmitting_end_idx = last_llist_idx; | |
2017 | ||
2018 | /* turn on clock(s), if they have been disabled */ | |
2019 | mddi_host_enable_hclk(); | |
2020 | mddi_host_enable_io_clock(); | |
2021 | pmhctl->int_type.llist_ptr_write_1++; | |
2022 | /* Write to primary pointer register */ | |
2023 | dma_coherent_pre_ops(); | |
2024 | mddi_host_reg_out(PRI_PTR, &llist_dma[first_llist_idx]); | |
2025 | ||
2026 | /* enable interrupt when complete */ | |
2027 | mddi_host_reg_outm(INTEN, MDDI_INT_PRI_LINK_LIST_DONE, | |
2028 | MDDI_INT_PRI_LINK_LIST_DONE); | |
2029 | ||
2030 | } else if (pmhctl->llist_info.waiting_start_idx == UNASSIGNED_INDEX) { | |
2031 | #ifndef FEATURE_MDDI_DISABLE_REVERSE | |
2032 | if (first_llist_idx == pmhctl->llist_info.reg_read_idx) { | |
2033 | /* | |
2034 | * we have a register read to send but need to wait | |
2035 | * for current reverse activity to end or there are | |
2036 | * packets currently transmitting | |
2037 | */ | |
2038 | /* mddi_rev_reg_read_attempt = 0; */ | |
2039 | pmhctl->llist_info.reg_read_waiting = TRUE; | |
2040 | } | |
2041 | #endif | |
2042 | ||
2043 | /* assign waiting index values */ | |
2044 | pmhctl->llist_info.waiting_start_idx = first_llist_idx; | |
2045 | pmhctl->llist_info.waiting_end_idx = last_llist_idx; | |
2046 | } else { | |
2047 | uint16 prev_end_idx = pmhctl->llist_info.waiting_end_idx; | |
2048 | #ifndef FEATURE_MDDI_DISABLE_REVERSE | |
2049 | if (first_llist_idx == pmhctl->llist_info.reg_read_idx) { | |
2050 | /* | |
2051 | * we have a register read to send but need to wait | |
2052 | * for current reverse activity to end or there are | |
2053 | * packets currently transmitting | |
2054 | */ | |
2055 | /* mddi_rev_reg_read_attempt = 0; */ | |
2056 | pmhctl->llist_info.reg_read_waiting = TRUE; | |
2057 | } | |
2058 | #endif | |
2059 | ||
2060 | llist = pmhctl->llist_ptr; | |
2061 | ||
2062 | /* clear end flag in previous last packet */ | |
2063 | llist[prev_end_idx].link_controller_flags = 0; | |
2064 | pmhctl->llist_notify[prev_end_idx].next_idx = first_llist_idx; | |
2065 | ||
2066 | /* set the next_packet_pointer of the previous last packet */ | |
2067 | llist[prev_end_idx].next_packet_pointer = | |
2068 | (void *)(&llist_dma[first_llist_idx]); | |
2069 | ||
2070 | /* clean cache so MDDI host can read data */ | |
2071 | memory_barrier(); | |
2072 | ||
2073 | /* assign new waiting last index value */ | |
2074 | pmhctl->llist_info.waiting_end_idx = last_llist_idx; | |
2075 | } | |
2076 | ||
2077 | spin_unlock_irqrestore(&mddi_host_spin_lock, flags); | |
2078 | ||
2079 | } | |
2080 | ||
2081 | void mddi_host_write_pix_attr_reg(uint32 value) | |
2082 | { | |
2083 | (void)value; | |
2084 | } | |
2085 | ||
2086 | void mddi_queue_reverse_encapsulation(boolean wait) | |
2087 | { | |
2088 | #ifdef FEATURE_MDDI_DISABLE_REVERSE | |
2089 | MDDI_MSG_CRIT("No reverse link available\n"); | |
2090 | (void)wait; | |
2091 | #else | |
2092 | unsigned long flags; | |
2093 | boolean error = FALSE; | |
2094 | mddi_host_type host_idx = MDDI_HOST_PRIM; | |
2095 | mddi_host_cntl_type *pmhctl = &(mhctl[MDDI_HOST_PRIM]); | |
2096 | ||
2097 | spin_lock_irqsave(&mddi_host_spin_lock, flags); | |
2098 | ||
2099 | /* turn on clock(s), if they have been disabled */ | |
2100 | mddi_host_enable_hclk(); | |
2101 | mddi_host_enable_io_clock(); | |
2102 | ||
2103 | if (wait) { | |
2104 | if (!mddi_rev_user.waiting) { | |
2105 | mddi_rev_user.waiting = TRUE; | |
2106 | INIT_COMPLETION(mddi_rev_user.done_comp); | |
2107 | } else | |
2108 | error = TRUE; | |
2109 | } | |
2110 | mddi_rev_encap_user_request = TRUE; | |
2111 | ||
2112 | if (pmhctl->rev_state == MDDI_REV_IDLE) { | |
2113 | /* attempt to send the reverse encapsulation now */ | |
2114 | mddi_host_type orig_host_idx = mddi_curr_host; | |
2115 | mddi_curr_host = host_idx; | |
2116 | mddi_issue_reverse_encapsulation(); | |
2117 | mddi_curr_host = orig_host_idx; | |
2118 | } | |
2119 | spin_unlock_irqrestore(&mddi_host_spin_lock, flags); | |
2120 | ||
2121 | if (error) { | |
2122 | MDDI_MSG_ERR("Reverse Encap request already in progress\n"); | |
2123 | } else if (wait) | |
2124 | wait_for_completion_killable(&(mddi_rev_user.done_comp)); | |
2125 | #endif | |
2126 | } | |
2127 | ||
2128 | /* ISR to be executed */ | |
2129 | boolean mddi_set_rev_handler(mddi_rev_handler_type handler, uint16 pkt_type) | |
2130 | { | |
2131 | #ifdef FEATURE_MDDI_DISABLE_REVERSE | |
2132 | MDDI_MSG_CRIT("No reverse link available\n"); | |
2133 | (void)handler; | |
2134 | (void)pkt_type; | |
2135 | return (FALSE); | |
2136 | #else | |
2137 | unsigned long flags; | |
2138 | uint16 hdlr; | |
2139 | boolean handler_set = FALSE; | |
2140 | boolean overwrite = FALSE; | |
2141 | mddi_host_type host_idx = MDDI_HOST_PRIM; | |
2142 | mddi_host_cntl_type *pmhctl = &(mhctl[MDDI_HOST_PRIM]); | |
2143 | ||
2144 | /* Disable interrupts */ | |
2145 | spin_lock_irqsave(&mddi_host_spin_lock, flags); | |
2146 | ||
2147 | for (hdlr = 0; hdlr < MAX_MDDI_REV_HANDLERS; hdlr++) { | |
2148 | if (mddi_rev_pkt_handler[hdlr].pkt_type == pkt_type) { | |
2149 | mddi_rev_pkt_handler[hdlr].handler = handler; | |
2150 | if (handler == NULL) { | |
2151 | /* clearing handler from table */ | |
2152 | mddi_rev_pkt_handler[hdlr].pkt_type = | |
2153 | INVALID_PKT_TYPE; | |
2154 | handler_set = TRUE; | |
2155 | if (pkt_type == 0x10) { /* video stream packet */ | |
2156 | /* ensure HCLK on to MDDI host core before register write */ | |
2157 | mddi_host_enable_hclk(); | |
2158 | /* No longer getting video, so reset rev encap size to default */ | |
2159 | pmhctl->rev_pkt_size = | |
2160 | MDDI_DEFAULT_REV_PKT_SIZE; | |
2161 | mddi_host_reg_out(REV_ENCAP_SZ, | |
2162 | pmhctl->rev_pkt_size); | |
2163 | } | |
2164 | } else { | |
2165 | /* already a handler for this packet */ | |
2166 | overwrite = TRUE; | |
2167 | } | |
2168 | break; | |
2169 | } | |
2170 | } | |
2171 | if ((hdlr >= MAX_MDDI_REV_HANDLERS) && (handler != NULL)) { | |
2172 | /* assigning new handler */ | |
2173 | for (hdlr = 0; hdlr < MAX_MDDI_REV_HANDLERS; hdlr++) { | |
2174 | if (mddi_rev_pkt_handler[hdlr].pkt_type == | |
2175 | INVALID_PKT_TYPE) { | |
2176 | if ((pkt_type == 0x10) && /* video stream packet */ | |
2177 | (pmhctl->rev_pkt_size < | |
2178 | MDDI_VIDEO_REV_PKT_SIZE)) { | |
2179 | /* ensure HCLK on to MDDI host core before register write */ | |
2180 | mddi_host_enable_hclk(); | |
2181 | /* Increase Rev Encap Size */ | |
2182 | pmhctl->rev_pkt_size = | |
2183 | MDDI_VIDEO_REV_PKT_SIZE; | |
2184 | mddi_host_reg_out(REV_ENCAP_SZ, | |
2185 | pmhctl->rev_pkt_size); | |
2186 | } | |
2187 | mddi_rev_pkt_handler[hdlr].handler = handler; | |
2188 | mddi_rev_pkt_handler[hdlr].pkt_type = pkt_type; | |
2189 | handler_set = TRUE; | |
2190 | break; | |
2191 | } | |
2192 | } | |
2193 | } | |
2194 | ||
2195 | /* Restore interrupts */ | |
2196 | spin_unlock_irqrestore(&mddi_host_spin_lock, flags); | |
2197 | ||
2198 | if (overwrite) | |
2199 | MDDI_MSG_ERR("Overwriting previous rev packet handler\n"); | |
2200 | ||
2201 | return handler_set; | |
2202 | ||
2203 | #endif | |
2204 | } /* mddi_set_rev_handler */ | |
2205 | ||
2206 | void mddi_host_disable_hibernation(boolean disable) | |
2207 | { | |
2208 | mddi_host_type host_idx = MDDI_HOST_PRIM; | |
2209 | mddi_host_cntl_type *pmhctl = &(mhctl[MDDI_HOST_PRIM]); | |
2210 | ||
2211 | if (disable) { | |
2212 | pmhctl->disable_hibernation = TRUE; | |
2213 | /* hibernation will be turned off by isr next time it is entered */ | |
2214 | } else { | |
2215 | if (pmhctl->disable_hibernation) { | |
2216 | unsigned long flags; | |
2217 | spin_lock_irqsave(&mddi_host_spin_lock, flags); | |
2218 | if (!MDDI_HOST_IS_HCLK_ON) | |
2219 | MDDI_HOST_ENABLE_HCLK; | |
2220 | mddi_host_reg_out(CMD, MDDI_CMD_HIBERNATE | 1); | |
2221 | spin_unlock_irqrestore(&mddi_host_spin_lock, flags); | |
2222 | pmhctl->disable_hibernation = FALSE; | |
2223 | } | |
2224 | } | |
2225 | } | |
2226 | ||
2227 | void mddi_mhctl_remove(mddi_host_type host_idx) | |
2228 | { | |
2229 | mddi_host_cntl_type *pmhctl; | |
2230 | ||
2231 | pmhctl = &(mhctl[host_idx]); | |
2232 | ||
2233 | dma_free_coherent(NULL, MDDI_LLIST_POOL_SIZE, (void *)pmhctl->llist_ptr, | |
2234 | pmhctl->llist_dma_addr); | |
2235 | ||
2236 | dma_free_coherent(NULL, MDDI_MAX_REV_DATA_SIZE, | |
2237 | (void *)pmhctl->rev_data_buf, | |
2238 | pmhctl->rev_data_dma_addr); | |
2239 | } |