diff options
| author | Hemant Gupta <hemant.gupta@stericsson.com> | 2011-05-30 20:13:57 +0530 |
|---|---|---|
| committer | Ulf Hansson <ulf.hansson@stericsson.com> | 2011-09-19 16:07:32 +0200 |
| commit | fffead9fa93f6eb87f1634c8f11ff7ddfda1f4b9 (patch) | |
| tree | f107fefbadb8c31ceb2a52a898fc020f65efa919 /drivers/media | |
| parent | 5397bc4d867e94ca1d4016371ac5e0e31c38a1e6 (diff) | |
CG2900 FM Radio: Handling Mono-Stereo Interrupt.
The linux FM driver for CG2900 now supports mono-stereo
transition interrupt from the CG2900. It indicates
stereo to mono transition or vice versa. Support for RDS
Interrupt, Scan Completion Interrupt, CG2900 Reset
Interrupt, etc is also added in this patch.
ST-Ericsson ID: 324264
ST-Ericsson Linux next: NA
ST-Ericsson FOSS-OUT ID: Trivial
Change-Id: I280a2a00a6f58e105d11f3a9f14ea662719c485e
Signed-off-by: Hemant Gupta <hemant.gupta@stericsson.com>
Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/30511
Reviewed-by: Johan PALMAEUS <johan.xj.palmaeus@stericsson.com>
Reviewed-by: Virupax SADASHIVPETIMATH <virupax.sadashivpetimath@stericsson.com>
Tested-by: Virupax SADASHIVPETIMATH <virupax.sadashivpetimath@stericsson.com>
Diffstat (limited to 'drivers/media')
| -rw-r--r-- | drivers/media/radio/CG2900/cg2900_fm_api.c | 324 | ||||
| -rw-r--r-- | drivers/media/radio/CG2900/cg2900_fm_api.h | 19 | ||||
| -rw-r--r-- | drivers/media/radio/CG2900/cg2900_fm_driver.c | 18 | ||||
| -rw-r--r--[-rwxr-xr-x] | drivers/media/radio/CG2900/cg2900_fm_driver.h | 5 | ||||
| -rw-r--r-- | drivers/media/radio/CG2900/radio-cg2900.c | 756 |
5 files changed, 680 insertions, 442 deletions
diff --git a/drivers/media/radio/CG2900/cg2900_fm_api.c b/drivers/media/radio/CG2900/cg2900_fm_api.c index b6be4e462e3..b7f5f9867d6 100644 --- a/drivers/media/radio/CG2900/cg2900_fm_api.c +++ b/drivers/media/radio/CG2900/cg2900_fm_api.c @@ -597,91 +597,108 @@ static void cg2900_fm_driver_callback( bool event_successful ) { + struct sk_buff *skb; + FM_INFO_REPORT("cg2900_fm_driver_callback: " "event = %02x, event_successful = %x", event, event_successful); - if (event_successful) { - switch (event) { - case FMD_EVENT_GEN_POWERUP: - FM_DEBUG_REPORT("FMD_EVENT_GEN_POWERUP"); - break; - case FMD_EVENT_ANTENNA_STATUS_CHANGED: - FM_DEBUG_REPORT("FMD_EVENT_ANTENNA_STATUS_CHANGED"); - break; - case FMD_EVENT_FREQUENCY_CHANGED: - FM_DEBUG_REPORT("FMD_EVENT_FREQUENCY_CHANGED "); - break; - - case FMD_EVENT_SEEK_STOPPED: - FM_DEBUG_REPORT("FMD_EVENT_SEEK_STOPPED"); - fm_event = CG2900_EVENT_SCAN_CANCELLED; - wake_up_poll_queue(); - break; - - case FMD_EVENT_SEEK_COMPLETED: - FM_DEBUG_REPORT("FMD_EVENT_SEEK_COMPLETED"); - fm_event = CG2900_EVENT_SEARCH_CHANNEL_FOUND; - wake_up_poll_queue(); - break; - - case FMD_EVENT_SCAN_BAND_COMPLETED: - FM_DEBUG_REPORT("FMD_EVENT_SCAN_BAND_COMPLETED"); - fm_event = CG2900_EVENT_SCAN_CHANNELS_FOUND; - wake_up_poll_queue(); - break; - - case FMD_EVENT_BLOCK_SCAN_COMPLETED: - FM_DEBUG_REPORT("FMD_EVENT_BLOCK_SCAN_COMPLETED"); - fm_event = CG2900_EVENT_BLOCK_SCAN_CHANNELS_FOUND; - wake_up_poll_queue(); - break; - - case FMD_EVENT_AF_UPDATE_SWITCH_COMPLETE: - FM_DEBUG_REPORT("FMD_EVENT_AF_UPDATE_SWITCH_COMPLETE"); - break; - - case FMD_EVENT_RDSGROUP_RCVD: - FM_DEBUG_REPORT("FMD_EVENT_RDSGROUP_RCVD"); - fmd_set_rds_sem(); - break; - - default: - FM_INFO_REPORT("cg2900_fm_driver_callback: " - "Unknown event = %x", event); - break; + switch (event) { + case FMD_EVENT_GEN_POWERUP: + FM_DEBUG_REPORT("FMD_EVENT_GEN_POWERUP"); + break; + case FMD_EVENT_ANTENNA_STATUS_CHANGED: + FM_DEBUG_REPORT("FMD_EVENT_ANTENNA_STATUS_CHANGED"); + break; + case FMD_EVENT_FREQUENCY_CHANGED: + FM_DEBUG_REPORT("FMD_EVENT_FREQUENCY_CHANGED "); + break; + case FMD_EVENT_SEEK_STOPPED: + FM_DEBUG_REPORT("FMD_EVENT_SEEK_STOPPED"); + skb = alloc_skb(SKB_FM_INTERRUPT_DATA, + GFP_KERNEL); + if (!skb) { + FM_ERR_REPORT("cg2900_fm_driver_callback: " + "Unable to Allocate Memory"); + return; } - } else { - switch (event) { - /* - * Seek stop, band scan, seek, block scan could - * fail for some reason so wake up poll queue - */ - case FMD_EVENT_SEEK_STOPPED: - FM_ERR_REPORT("FMD_EVENT_SEEK_STOPPED"); - fm_event = CG2900_EVENT_SCAN_CANCELLED; - wake_up_poll_queue(); - break; - case FMD_EVENT_SEEK_COMPLETED: - FM_ERR_REPORT("FMD_EVENT_SEEK_COMPLETED"); - fm_event = CG2900_EVENT_SEARCH_CHANNEL_FOUND; - wake_up_poll_queue(); - break; - case FMD_EVENT_SCAN_BAND_COMPLETED: - FM_ERR_REPORT("FMD_EVENT_SCAN_BAND_COMPLETED"); - fm_event = CG2900_EVENT_SCAN_CHANNELS_FOUND; - wake_up_poll_queue(); - break; - case FMD_EVENT_BLOCK_SCAN_COMPLETED: - FM_ERR_REPORT("FMD_EVENT_BLOCK_SCAN_COMPLETED"); - fm_event = CG2900_EVENT_BLOCK_SCAN_CHANNELS_FOUND; - wake_up_poll_queue(); - break; - default: + skb->data[0] = CG2900_EVENT_SCAN_CANCELLED; + skb->data[1] = event_successful; + skb_queue_tail(&fm_interrupt_queue, skb); + wake_up_poll_queue(); + break; + case FMD_EVENT_SEEK_COMPLETED: + FM_DEBUG_REPORT("FMD_EVENT_SEEK_COMPLETED"); + skb = alloc_skb(SKB_FM_INTERRUPT_DATA, + GFP_KERNEL); + if (!skb) { FM_ERR_REPORT("cg2900_fm_driver_callback: " - "event = %x failed!!!!", event); - break; + "Unable to Allocate Memory"); + return; + } + skb->data[0] = CG2900_EVENT_SEARCH_CHANNEL_FOUND; + skb->data[1] = event_successful; + skb_queue_tail(&fm_interrupt_queue, skb); + wake_up_poll_queue(); + break; + case FMD_EVENT_SCAN_BAND_COMPLETED: + FM_DEBUG_REPORT("FMD_EVENT_SCAN_BAND_COMPLETED"); + skb = alloc_skb(SKB_FM_INTERRUPT_DATA, + GFP_KERNEL); + if (!skb) { + FM_ERR_REPORT("cg2900_fm_driver_callback: " + "Unable to Allocate Memory"); + return; } + skb->data[0] = CG2900_EVENT_SCAN_CHANNELS_FOUND; + skb->data[1] = event_successful; + skb_queue_tail(&fm_interrupt_queue, skb); + wake_up_poll_queue(); + break; + case FMD_EVENT_BLOCK_SCAN_COMPLETED: + FM_DEBUG_REPORT("FMD_EVENT_BLOCK_SCAN_COMPLETED"); + skb = alloc_skb(SKB_FM_INTERRUPT_DATA, + GFP_KERNEL); + if (!skb) { + FM_ERR_REPORT("cg2900_fm_driver_callback: " + "Unable to Allocate Memory"); + return; + } + skb->data[0] = CG2900_EVENT_BLOCK_SCAN_CHANNELS_FOUND; + skb->data[1] = event_successful; + skb_queue_tail(&fm_interrupt_queue, skb); + wake_up_poll_queue(); + break; + case FMD_EVENT_AF_UPDATE_SWITCH_COMPLETE: + FM_DEBUG_REPORT("FMD_EVENT_AF_UPDATE_SWITCH_COMPLETE"); + break; + case FMD_EVENT_RDSGROUP_RCVD: + FM_DEBUG_REPORT("FMD_EVENT_RDSGROUP_RCVD"); + /* + * Release the rds semaphore, poll queue + * will be woken-up in rds callback + */ + fmd_set_rds_sem(); + break; + case FMD_EVENT_MONO_STEREO_TRANSITION_COMPLETE: + FM_ERR_REPORT( + "FMD_EVENT_MONO_STEREO_TRANSITION_COMPLETE"); + skb = alloc_skb(SKB_FM_INTERRUPT_DATA, + GFP_KERNEL); + if (!skb) { + FM_ERR_REPORT("cg2900_fm_driver_callback: " + "Unable to Allocate Memory"); + return; + } + skb->data[0] = CG2900_EVENT_MONO_STEREO_TRANSITION; + skb->data[1] = event_successful; + skb_queue_tail(&fm_interrupt_queue, skb); + wake_up_poll_queue(); + break; + default: + FM_INFO_REPORT("cg2900_fm_driver_callback: " + "Unknown event = %x", event); + break; } } @@ -696,6 +713,8 @@ static void cg2900_fm_rds_callback(void) u8 index = 0; u16 rds_local_buf_count; int result; + struct sk_buff *skb; + FM_INFO_REPORT("cg2900_fm_rds_callback"); /* @@ -751,7 +770,21 @@ static void cg2900_fm_rds_callback(void) fm_rds_info.rds_head++; if (fm_rds_info.rds_head == MAX_RDS_BUFFER) fm_rds_info.rds_head = 0; - wake_up_read_queue(); + + /* Queue the RDS event */ + skb = alloc_skb(SKB_FM_INTERRUPT_DATA, + GFP_KERNEL); + if (!skb) { + FM_ERR_REPORT("cg2900_fm_rds_callback: " + "Unable to Allocate Memory"); + goto error; + } + skb->data[0] = CG2900_EVENT_RDS_EVENT; + skb->data[1] = true; + skb_queue_tail(&fm_interrupt_queue, skb); + + /* Wake up the poll queue */ + wake_up_poll_queue(); error: mutex_unlock(&rds_mutex); } @@ -879,10 +912,11 @@ int cg2900_fm_switch_on( fm_state = CG2900_FM_STATE_SWITCHED_ON; fm_mode = CG2900_FM_IDLE_MODE; - memset(&fm_rds_info, 0, sizeof(struct cg2900_fm_rds_info)); + memset(&fm_rds_info, 0, + sizeof(struct cg2900_fm_rds_info)); memset(fm_rds_buf, 0, - sizeof(struct cg2900_fm_rds_buf) * - MAX_RDS_BUFFER * MAX_RDS_GROUPS); + sizeof(struct cg2900_fm_rds_buf) * + MAX_RDS_BUFFER * MAX_RDS_GROUPS); error: FM_DEBUG_REPORT("cg2900_fm_switch_on: returning %d", @@ -931,10 +965,12 @@ int cg2900_fm_switch_off(void) fm_state = CG2900_FM_STATE_INITIALIZED; fm_mode = CG2900_FM_IDLE_MODE; memset(&fm_rds_info, 0, - sizeof(struct cg2900_fm_rds_info)); + sizeof(struct cg2900_fm_rds_info)); memset(fm_rds_buf, 0, - sizeof(struct cg2900_fm_rds_buf) * - MAX_RDS_BUFFER * MAX_RDS_GROUPS); + sizeof(struct cg2900_fm_rds_buf) * + MAX_RDS_BUFFER * MAX_RDS_GROUPS); + /* Remove all Interrupts from the queue */ + skb_queue_purge(&fm_interrupt_queue); } error: @@ -955,6 +991,13 @@ int cg2900_fm_standby(void) result = -EINVAL; goto error; } + memset(&fm_rds_info, 0, + sizeof(struct cg2900_fm_rds_info)); + memset(fm_rds_buf, 0, + sizeof(struct cg2900_fm_rds_buf) * + MAX_RDS_BUFFER * MAX_RDS_GROUPS); + /* Remove all Interrupts from the queue */ + skb_queue_purge(&fm_interrupt_queue); result = fmd_goto_standby(); if (0 != result) { FM_ERR_REPORT("cg2900_fm_standby: " @@ -1118,6 +1161,9 @@ int cg2900_fm_set_rx_default_settings( goto error; } + /* Remove all Interrupt from the queue */ + skb_queue_purge(&fm_interrupt_queue); + FM_DEBUG_REPORT("cg2900_fm_set_rx_default_settings: " "Sending Set rds"); @@ -1194,13 +1240,15 @@ int cg2900_fm_set_tx_default_settings( fm_rds_status = false; fmd_stop_rds_thread(); memset(&fm_rds_info, 0, - sizeof(struct cg2900_fm_rds_info)); + sizeof(struct cg2900_fm_rds_info)); memset(fm_rds_buf, 0, - sizeof(struct cg2900_fm_rds_buf) * - MAX_RDS_BUFFER * MAX_RDS_GROUPS); + sizeof(struct cg2900_fm_rds_buf) * + MAX_RDS_BUFFER * MAX_RDS_GROUPS); /* Give 50 ms delay to exit the RDS thread */ schedule_timeout_interruptible(msecs_to_jiffies(50)); } + /* Remove all Interrupt from the queue */ + skb_queue_purge(&fm_interrupt_queue); /* Switch To Tx mode */ FM_DEBUG_REPORT("cg2900_fm_set_tx_default_settings: " @@ -1396,6 +1444,14 @@ int cg2900_fm_search_up_freq(void) /* Stop RDS if it is active */ result = cg2900_fm_rds_off(); fm_prev_rds_status = true; + } else { + memset(&fm_rds_info, 0, + sizeof(struct cg2900_fm_rds_info)); + memset(fm_rds_buf, 0, + sizeof(struct cg2900_fm_rds_buf) * + MAX_RDS_BUFFER * MAX_RDS_GROUPS); + /* Remove all Interrupts from the queue */ + skb_queue_purge(&fm_interrupt_queue); } result = fmd_rx_seek(CG2900_DIR_UP); if (0 != result) { @@ -1404,11 +1460,6 @@ int cg2900_fm_search_up_freq(void) result = -EINVAL; goto error; } - memset(&fm_rds_info, 0, - sizeof(struct cg2900_fm_rds_info)); - memset(fm_rds_buf, 0, - sizeof(struct cg2900_fm_rds_buf) * - MAX_RDS_BUFFER * MAX_RDS_GROUPS); result = 0; error: @@ -1433,6 +1484,14 @@ int cg2900_fm_search_down_freq(void) /* Stop RDS if it is active */ result = cg2900_fm_rds_off(); fm_prev_rds_status = true; + } else { + memset(&fm_rds_info, 0, + sizeof(struct cg2900_fm_rds_info)); + memset(fm_rds_buf, 0, + sizeof(struct cg2900_fm_rds_buf) * + MAX_RDS_BUFFER * MAX_RDS_GROUPS); + /* Remove all Interrupts from the queue */ + skb_queue_purge(&fm_interrupt_queue); } result = fmd_rx_seek(CG2900_DIR_DOWN); if (0 != result) { @@ -1441,11 +1500,6 @@ int cg2900_fm_search_down_freq(void) result = -EINVAL; goto error; } - memset(&fm_rds_info, 0, - sizeof(struct cg2900_fm_rds_info)); - memset(fm_rds_buf, 0, - sizeof(struct cg2900_fm_rds_buf) * - MAX_RDS_BUFFER * MAX_RDS_GROUPS); result = 0; error: @@ -1470,6 +1524,14 @@ int cg2900_fm_start_band_scan(void) /* Stop RDS if it is active */ result = cg2900_fm_rds_off(); fm_prev_rds_status = true; + } else { + memset(&fm_rds_info, 0, + sizeof(struct cg2900_fm_rds_info)); + memset(fm_rds_buf, 0, + sizeof(struct cg2900_fm_rds_buf) * + MAX_RDS_BUFFER * MAX_RDS_GROUPS); + /* Remove all Interrupts from the queue */ + skb_queue_purge(&fm_interrupt_queue); } result = fmd_rx_scan_band(DEFAULT_CHANNELS_TO_SCAN); if (0 != result) { @@ -1478,11 +1540,6 @@ int cg2900_fm_start_band_scan(void) result = -EINVAL; goto error; } - memset(&fm_rds_info, 0, - sizeof(struct cg2900_fm_rds_info)); - memset(fm_rds_buf, 0, - sizeof(struct cg2900_fm_rds_buf) * - MAX_RDS_BUFFER * MAX_RDS_GROUPS); result = 0; error: @@ -1648,6 +1705,14 @@ int cg2900_fm_start_block_scan( /* Stop RDS if it is active */ result = cg2900_fm_rds_off(); fm_prev_rds_status = true; + } else { + memset(&fm_rds_info, 0, + sizeof(struct cg2900_fm_rds_info)); + memset(fm_rds_buf, 0, + sizeof(struct cg2900_fm_rds_buf) * + MAX_RDS_BUFFER * MAX_RDS_GROUPS); + /* Remove all Interrupts from the queue */ + skb_queue_purge(&fm_interrupt_queue); } result = fmd_get_antenna( &antenna); @@ -1661,11 +1726,6 @@ int cg2900_fm_start_block_scan( result = -EINVAL; goto error; } - memset(&fm_rds_info, 0, - sizeof(struct cg2900_fm_rds_info)); - memset(fm_rds_buf, 0, - sizeof(struct cg2900_fm_rds_buf) * - MAX_RDS_BUFFER * MAX_RDS_GROUPS); result = 0; error: @@ -1990,8 +2050,8 @@ int cg2900_fm_tx_get_pilot_tone_status( result = fmd_tx_get_stereo_mode(enable); if (0 != result) { FM_ERR_REPORT("cg2900_fm_tx_get_pilot_tone_status: " - "fmd_tx_get_stereo_mode failed %d", - (unsigned int)result); + "fmd_tx_get_stereo_mode failed %d", + result); result = -EINVAL; goto error; } @@ -2334,6 +2394,13 @@ int cg2900_fm_rds_off(void) result = -EINVAL; goto error; } + memset(&fm_rds_info, 0, + sizeof(struct cg2900_fm_rds_info)); + memset(fm_rds_buf, 0, + sizeof(struct cg2900_fm_rds_buf) * + MAX_RDS_BUFFER * MAX_RDS_GROUPS); + /* Remove all Interrupts from the queue */ + skb_queue_purge(&fm_interrupt_queue); result = fmd_rx_set_rds(FMD_SWITCH_OFF_RDS); if (0 != result) { FM_ERR_REPORT("cg2900_fm_rds_off: fmd_rx_set_rds failed, " @@ -2343,11 +2410,6 @@ int cg2900_fm_rds_off(void) } /* Stop the RDS Thread */ fm_rds_status = false; - memset(&fm_rds_info, 0, - sizeof(struct cg2900_fm_rds_info)); - memset(fm_rds_buf, 0, - sizeof(struct cg2900_fm_rds_buf) * - MAX_RDS_BUFFER * MAX_RDS_GROUPS); FM_DEBUG_REPORT("cg2900_fm_rds_off: " "Stopping RDS Thread"); fmd_stop_rds_thread(); @@ -2399,10 +2461,10 @@ int cg2900_fm_rds_on(void) /* Start the RDS Thread to read the RDS Buffers */ fm_rds_status = true; memset(&fm_rds_info, 0, - sizeof(struct cg2900_fm_rds_info)); + sizeof(struct cg2900_fm_rds_info)); memset(fm_rds_buf, 0, - sizeof(struct cg2900_fm_rds_buf) * - MAX_RDS_BUFFER * MAX_RDS_GROUPS); + sizeof(struct cg2900_fm_rds_buf) * + MAX_RDS_BUFFER * MAX_RDS_GROUPS); fmd_start_rds_thread(cg2900_fm_rds_callback); error: @@ -2584,6 +2646,13 @@ int cg2900_fm_set_frequency( result = -EINVAL; goto error; } + memset(&fm_rds_info, 0, + sizeof(struct cg2900_fm_rds_info)); + memset(fm_rds_buf, 0, + sizeof(struct cg2900_fm_rds_buf) * + MAX_RDS_BUFFER * MAX_RDS_GROUPS); + /* Remove all Interrupts from the queue */ + skb_queue_purge(&fm_interrupt_queue); if (CG2900_FM_RX_MODE == fm_mode) { FM_DEBUG_REPORT("cg2900_fm_set_frequency: " "fmd_rx_set_frequency"); @@ -2616,11 +2685,6 @@ int cg2900_fm_set_frequency( result = -EINVAL; goto error; } - memset(&fm_rds_info, 0, - sizeof(struct cg2900_fm_rds_info)); - memset(fm_rds_buf, 0, - sizeof(struct cg2900_fm_rds_buf) * - MAX_RDS_BUFFER * MAX_RDS_GROUPS); result = 0; } @@ -2820,16 +2884,7 @@ int cg2900_fm_get_mode( FM_DEBUG_REPORT("cg2900_fm_get_mode: " "fmd_rx_get_stereo_mode"); result = fmd_rx_get_stereo_mode(cur_mode); - switch (*cur_mode) { - case FMD_STEREOMODE_OFF: - case FMD_STEREOMODE_BLENDING: - *cur_mode = CG2900_MODE_STEREO; - break; - case FMD_STEREOMODE_MONO: - default: - *cur_mode = CG2900_MODE_MONO; - break; - } + FM_DEBUG_REPORT("cg2900_fm_get_mode: cur_mode = %x", *cur_mode); } else if (CG2900_FM_TX_MODE == fm_mode) { FM_DEBUG_REPORT("cg2900_fm_get_mode: " "fmd_tx_get_stereo_mode"); @@ -3024,8 +3079,7 @@ int cg2900_fm_set_test_tone_generator( if (0 != result) { FM_ERR_REPORT("cg2900_fm_set_test_tone_generator: " "fmd_set_test_tone_generator_status failed" - ", Error Code %d", - result); + ", Error Code %d", result); result = -EINVAL; goto error; } diff --git a/drivers/media/radio/CG2900/cg2900_fm_api.h b/drivers/media/radio/CG2900/cg2900_fm_api.h index 747cdbc1e0d..ec9e6e86f77 100644 --- a/drivers/media/radio/CG2900/cg2900_fm_api.h +++ b/drivers/media/radio/CG2900/cg2900_fm_api.h @@ -12,10 +12,13 @@ #define CG2900_FM_API_H #include <linux/device.h> +#include <linux/skbuff.h> /* Callback function to receive RDS Data. */ typedef void (*cg2900_fm_rds_cb)(void); +extern struct sk_buff_head fm_interrupt_queue; + /** * struct cg2900_fm_rds_buf - RDS Group Receiving Structure * @@ -145,6 +148,9 @@ enum cg2900_fm_grid { * @CG2900_EVENT_SCAN_CHANNELS_FOUND: Band Scan is completed. * @CG2900_EVENT_BLOCK_SCAN_CHANNELS_FOUND: Block Scan is completed. * @CG2900_EVENT_SCAN_CANCELLED: Scan/Seek is cancelled. + * @CG2900_EVENT_MONO_STEREO_TRANSITION: Mono/Stereo Transition has taken place. + * @CG2900_EVENT_DEVICE_RESET: CG2900 has been reset by some other IP. + * @CG2900_EVENT_RDS_EVENT: RDS data interrupt has been received from chip. * * Various Events reported by FM API layer. */ @@ -153,7 +159,10 @@ enum cg2900_fm_event { CG2900_EVENT_SEARCH_CHANNEL_FOUND, CG2900_EVENT_SCAN_CHANNELS_FOUND, CG2900_EVENT_BLOCK_SCAN_CHANNELS_FOUND, - CG2900_EVENT_SCAN_CANCELLED + CG2900_EVENT_SCAN_CANCELLED, + CG2900_EVENT_MONO_STEREO_TRANSITION, + CG2900_EVENT_DEVICE_RESET, + CG2900_EVENT_RDS_EVENT }; /** @@ -197,6 +206,7 @@ enum cg2900_fm_stereo_mode { #define DEFAULT_CHANNELS_TO_SCAN 32 #define MAX_CHANNELS_TO_SCAN 99 #define MAX_CHANNELS_FOR_BLOCK_SCAN 198 +#define SKB_FM_INTERRUPT_DATA 2 extern u8 fm_event; extern struct cg2900_fm_rds_buf fm_rds_buf[MAX_RDS_BUFFER][MAX_RDS_GROUPS]; @@ -978,13 +988,6 @@ void cg2900_handle_device_reset(void); void wake_up_poll_queue(void); /** - * void wake_up_read_queue()- Wakes up the Task waiting on Read Queue. - * This function is called when RDS data is available for reading by - * application. - */ -void wake_up_read_queue(void); - -/** * void cg2900_fm_set_chip_version()- Sets the Version of the Controller. * * This function is used to update the Chip Version information at time diff --git a/drivers/media/radio/CG2900/cg2900_fm_driver.c b/drivers/media/radio/CG2900/cg2900_fm_driver.c index 6ac380409c1..9959790cbe8 100644 --- a/drivers/media/radio/CG2900/cg2900_fm_driver.c +++ b/drivers/media/radio/CG2900/cg2900_fm_driver.c @@ -2309,6 +2309,9 @@ int fmd_rx_get_stereo_mode( ) { int err; + int io_result; + u16 response_count; + u16 response_data[CMD_RP_GET_STATE_RSP_PARAM_LEN]; if (fmd_go_cmd_busy()) { err = -EBUSY; @@ -2325,7 +2328,20 @@ int fmd_rx_get_stereo_mode( goto error; } - *mode = fmd_state_info.rx_stereo_mode; + io_result = fmd_send_cmd_and_read_resp( + CMD_FMR_RP_GET_STATE, + CMD_RP_GET_STATE_PARAM_LEN, + NULL, + &response_count, + response_data); + + if (io_result != 0) { + err = io_result; + goto error; + } + + /* 2nd element of response is stereo signal */ + *mode = response_data[1]; err = 0; error: diff --git a/drivers/media/radio/CG2900/cg2900_fm_driver.h b/drivers/media/radio/CG2900/cg2900_fm_driver.h index 313824a1e00..fc20476c64a 100755..100644 --- a/drivers/media/radio/CG2900/cg2900_fm_driver.h +++ b/drivers/media/radio/CG2900/cg2900_fm_driver.h @@ -173,6 +173,7 @@ enum fmd_debug_levels { #define CMD_FMR_DP_BUFFER_SET_THRESHOLD 0x06C3 #define CMD_FMR_DP_SET_CONTROL 0x02A3 #define CMD_FMR_RP_GET_RSSI 0x0083 +#define CMD_FMR_RP_GET_STATE 0x0063 #define CMD_FMR_RP_STEREO_SET_MODE 0x0123 #define CMD_FMR_SET_ANTENNA 0x0663 #define CMD_FMR_SP_AF_SWITCH_GET_RESULT 0x0603 @@ -218,7 +219,7 @@ enum fmd_debug_levels { #define CMD_GEN_SET_REGISTER_VALUE 0x0101 #define CMD_TST_TONE_ENABLE 0x0027 #define CMD_TST_TONE_CONNECT 0x0047 -#define CMD_TST_TONE_SET_PARAMS 0x0067 +#define CMD_TST_TONE_SET_PARAMS 0x0067 /* FM Command Id Parameter Length */ #define CMD_GET_VERSION_PARAM_LEN 0 @@ -233,6 +234,8 @@ enum fmd_debug_levels { #define CMD_RP_STEREO_SET_MODE_PARAM_LEN 1 #define CMD_RP_GET_RSSI_PARAM_LEN 0 #define CMD_RP_GET_RSSI_RSP_PARAM_LEN 1 +#define CMD_RP_GET_STATE_PARAM_LEN 0 +#define CMD_RP_GET_STATE_RSP_PARAM_LEN 2 #define CMD_SP_SEARCH_START_PARAM_LEN 4 #define CMD_SP_SCAN_START_PARAM_LEN 4 #define CMD_SP_SCAN_GET_RESULT_PARAM_LEN 1 diff --git a/drivers/media/radio/CG2900/radio-cg2900.c b/drivers/media/radio/CG2900/radio-cg2900.c index 4fe3e6ffd5c..6a74c0ac257 100644 --- a/drivers/media/radio/CG2900/radio-cg2900.c +++ b/drivers/media/radio/CG2900/radio-cg2900.c @@ -17,6 +17,7 @@ #include <linux/platform_device.h> #include<linux/string.h> #include<linux/wait.h> +#include<linux/mfd/cg2900.h> #include"cg2900_fm_driver.h" #define RADIO_CG2900_VERSION KERNEL_VERSION(1, 1, 0) @@ -35,6 +36,7 @@ #define FMR_USA_GRID_IN_HZ 200000 #define FMR_AF_SWITCH_DATA_SIZE 2 #define FMR_BLOCK_SCAN_DATA_SIZE 2 +#define FMR_GET_INTERRUPT_DATA_SIZE 2 #define FMR_TEST_TONE_CONNECT_DATA_SIZE 2 #define FMR_TEST_TONE_SET_PARAMS_DATA_SIZE 6 @@ -148,6 +150,9 @@ static void cg2900_convert_err_to_v4l2( char status_byte, char *out_byte ); +static int cg2900_map_event_to_v4l2( + u8 fm_event + ); static u32 freq_low; static u32 freq_high; @@ -160,8 +165,7 @@ static int band; /* cg2900_poll_queue - Main Wait Queue for polling (Scan/Seek) */ static wait_queue_head_t cg2900_poll_queue; -/* cg2900_read_queue - Main Wait Queue for receiving RDS Data */ -static DECLARE_WAIT_QUEUE_HEAD(cg2900_read_queue); +struct sk_buff_head fm_interrupt_queue; /** * enum fm_seek_status - Seek status of FM Radio. @@ -378,7 +382,7 @@ static int vidioc_get_tuner( status = cg2900_fm_get_mode(&mode); - FM_DEBUG_REPORT("vidioc_get_tuner: mode = %d, ", mode); + FM_DEBUG_REPORT("vidioc_get_tuner: mode = %x, ", mode); if (0 != status) { /* Get mode API failed, set mode to mono */ @@ -612,7 +616,7 @@ static int vidioc_get_modulator( if (cg2900_device.fm_mode == CG2900_FM_TX_MODE) { status = cg2900_fm_get_mode(&mode); - FM_DEBUG_REPORT("vidioc_get_modulator: mode = %d", mode); + FM_DEBUG_REPORT("vidioc_get_modulator: mode = %x", mode); if (0 != status) { /* Get mode API failed, set mode to mono */ modulator->txsubchans = V4L2_TUNER_SUB_MONO; @@ -795,6 +799,7 @@ static int vidioc_get_frequency( int status; u32 frequency; int ret_val = -EINVAL; + struct sk_buff *skb; FM_INFO_REPORT("vidioc_get_frequency: Status = %d", cg2900_device.seekstatus); @@ -807,6 +812,26 @@ static int vidioc_get_frequency( } if (cg2900_device.seekstatus == FMR_SEEK_IN_PROGRESS) { + if (skb_queue_empty(&fm_interrupt_queue)) { + /* No Interrupt, bad case */ + FM_ERR_REPORT("vidioc_get_frequency: " + "No Interrupt to read"); + fm_event = CG2900_EVENT_NO_EVENT; + goto error; + } + spin_lock(&fm_spinlock); + skb = skb_dequeue(&fm_interrupt_queue); + spin_unlock(&fm_spinlock); + if (!skb) { + /* No Interrupt, bad case */ + FM_ERR_REPORT("vidioc_get_frequency: " + "No Interrupt to read"); + fm_event = CG2900_EVENT_NO_EVENT; + goto error; + } + fm_event = (u8)skb->data[0]; + FM_DEBUG_REPORT("vidioc_get_frequency: Interrupt = %x", + fm_event); /* Check if seek is finished or not */ if (CG2900_EVENT_SEARCH_CHANNEL_FOUND == fm_event) { /* seek is finished */ @@ -815,6 +840,12 @@ static int vidioc_get_frequency( freq->frequency = cg2900_device.frequency; cg2900_device.seekstatus = FMR_SEEK_NONE; fm_event = CG2900_EVENT_NO_EVENT; + kfree_skb(skb); + spin_unlock(&fm_spinlock); + } else { + /* Some other interrupt, queue it back */ + spin_lock(&fm_spinlock); + skb_queue_head(&fm_interrupt_queue, skb); spin_unlock(&fm_spinlock); } } else { @@ -991,7 +1022,7 @@ static int vidioc_query_ctrl( default: FM_DEBUG_REPORT("vidioc_query_ctrl: " - "--> unsupported id = %d", (int)query_ctrl->id); + "--> unsupported id = %x", query_ctrl->id); break; } @@ -1053,7 +1084,7 @@ static int vidioc_get_ctrl( break; case V4L2_CID_CG2900_RADIO_SELECT_ANTENNA: status = cg2900_fm_get_antenna(&antenna); - FM_DEBUG_REPORT("vidioc_get_ctrl: Antenna = %d", antenna); + FM_DEBUG_REPORT("vidioc_get_ctrl: Antenna = %x", antenna); if (0 == status) { ctrl->value = antenna; ret_val = 0; @@ -1061,7 +1092,7 @@ static int vidioc_get_ctrl( break; case V4L2_CID_CG2900_RADIO_RDS_AF_UPDATE_GET_RESULT: status = cg2900_fm_af_update_get_result(&rssi); - FM_DEBUG_REPORT("vidioc_get_ctrl: AF RSSI Level = %d", rssi); + FM_DEBUG_REPORT("vidioc_get_ctrl: AF RSSI Level = %x", rssi); if (0 == status) { ctrl->value = rssi; ret_val = 0; @@ -1069,7 +1100,7 @@ static int vidioc_get_ctrl( break; case V4L2_CID_CG2900_RADIO_RDS_AF_SWITCH_GET_RESULT: status = cg2900_fm_af_switch_get_result(&conclusion); - FM_DEBUG_REPORT("vidioc_get_ctrl: AF Switch conclusion = %d", + FM_DEBUG_REPORT("vidioc_get_ctrl: AF Switch conclusion = %x", conclusion); if (0 != status) break; @@ -1086,13 +1117,13 @@ static int vidioc_get_ctrl( */ ctrl->value = -conclusion; FM_ERR_REPORT("vidioc_get_ctrl: " - "AF-Switch failed with value %d", (__s32)ctrl->value); + "AF-Switch failed with value %d", ctrl->value); ret_val = 0; } break; default: FM_DEBUG_REPORT("vidioc_get_ctrl: " - "unsupported (id = %d)", (int)ctrl->id); + "unsupported (id = %x)", (int)ctrl->id); ret_val = -EINVAL; } FM_DEBUG_REPORT("vidioc_get_ctrl: returning = %d", @@ -1242,7 +1273,8 @@ static int vidioc_set_ctrl( "V4L2_CID_CG2900_RADIO_TEST_TONE_GENERATOR_SET_STATUS " "state = %d ", ctrl->value); if (ctrl->value < V4L2_CG2900_RADIO_TEST_TONE_GEN_OFF || - ctrl->value > V4L2_CG2900_RADIO_TEST_TONE_GENERATOR_ON_WO_SRC) { + ctrl->value > + V4L2_CG2900_RADIO_TEST_TONE_GENERATOR_ON_WO_SRC) { FM_ERR_REPORT("Invalid parameter = %d", ctrl->value); break; } @@ -1253,8 +1285,7 @@ static int vidioc_set_ctrl( case V4L2_CID_CG2900_RADIO_TUNE_DEEMPHASIS: FM_DEBUG_REPORT("vidioc_set_ctrl: " "V4L2_CID_CG2900_RADIO_TUNE_DEEMPHASIS, " - "Value = %d", - ctrl->value); + "Value = %d", ctrl->value); if ((V4L2_CG2900_RADIO_DEEMPHASIS_DISABLED > ctrl->value) || @@ -1285,7 +1316,7 @@ static int vidioc_set_ctrl( break; default: FM_DEBUG_REPORT("vidioc_set_ctrl: " - "unsupported (id = %d)", (int)ctrl->id); + "unsupported (id = %x)", ctrl->id); } FM_DEBUG_REPORT("vidioc_set_ctrl: returning = %d", ret_val); @@ -1321,6 +1352,10 @@ static int vidioc_get_ext_ctrls( int count = 0; int ret_val = -EINVAL; int status; + struct sk_buff *skb; + u8 mode; + s8 interrupt_success; + int *fm_interrupt_buffer; FM_INFO_REPORT("vidioc_get_ext_ctrls: Id = %04x," "ext_ctrl->ctrl_class = %04x", @@ -1336,217 +1371,340 @@ static int vidioc_get_ext_ctrls( switch (ext_ctrl->controls->id) { case V4L2_CID_CG2900_RADIO_BANDSCAN_GET_RESULTS: - { - if (ext_ctrl->ctrl_class != V4L2_CTRL_CLASS_USER) { - FM_ERR_REPORT("vidioc_get_ext_ctrls: " - "V4L2_CID_CG2900_RADIO_BANDSCAN_GET_RESULTS " - "Unsupported ctrl_class = %04x", - ext_ctrl->ctrl_class); + if (ext_ctrl->ctrl_class != V4L2_CTRL_CLASS_USER) { + FM_ERR_REPORT("vidioc_get_ext_ctrls: " + "V4L2_CID_CG2900_RADIO_BANDSCAN_GET_RESULTS " + "Unsupported ctrl_class = %04x", + ext_ctrl->ctrl_class); + break; + } + if (cg2900_device.seekstatus == + FMR_SEEK_IN_PROGRESS) { + spin_lock(&fm_spinlock); + skb = skb_dequeue(&fm_interrupt_queue); + spin_unlock(&fm_spinlock); + if (!skb) { + /* No Interrupt, bad case */ + FM_ERR_REPORT("No Interrupt to read"); + fm_event = CG2900_EVENT_NO_EVENT; break; } - if (cg2900_device.seekstatus == - FMR_SEEK_IN_PROGRESS) { - if (fm_event == - CG2900_EVENT_SCAN_CHANNELS_FOUND) { - /* Check to get Scan Result */ - status = - cg2900_fm_get_scan_result - (&no_of_scan_freq, scanfreq, - scanfreq_rssi_level); - if (0 != status) { - FM_ERR_REPORT - ("vidioc_get_ext_ctrls: " - "cg2900_fm_get_scan_" - "result: returned %d", - status); - break; - } + fm_event = (u8)skb->data[0]; + FM_DEBUG_REPORT( + "V4L2_CID_CG2900_RADIO" + "_BANDSCAN_GET_RESULTS: " + "fm_event = %x", fm_event); + if (fm_event == + CG2900_EVENT_SCAN_CHANNELS_FOUND) { + /* Check to get Scan Result */ + status = + cg2900_fm_get_scan_result + (&no_of_scan_freq, scanfreq, + scanfreq_rssi_level); + if (0 != status) { + FM_ERR_REPORT + ("vidioc_get_ext_ctrls: " + "cg2900_fm_get_scan_" + "result: returned %d", + status); + kfree_skb(skb); + break; } + kfree_skb(skb); + } else { + /* Some other interrupt, Queue it back */ + spin_lock(&fm_spinlock); + skb_queue_head(&fm_interrupt_queue, skb); + spin_unlock(&fm_spinlock); } - FM_DEBUG_REPORT("vidioc_get_ext_ctrls: " - "SeekStatus = %d, GlobalEvent = %d, " - "numchannels = %d", - cg2900_device.seekstatus, - fm_event, no_of_scan_freq); - - if (ext_ctrl->controls->size == 0 && - ext_ctrl->controls->string == NULL) { - if (cg2900_device.seekstatus == - FMR_SEEK_IN_PROGRESS && - CG2900_EVENT_SCAN_CHANNELS_FOUND - == fm_event) { - spin_lock(&fm_spinlock); - ext_ctrl->controls->size = - no_of_scan_freq; - cg2900_device.seekstatus - = FMR_SEEK_NONE; - fm_event = - CG2900_EVENT_NO_EVENT; - spin_unlock(&fm_spinlock); - return -ENOSPC; - } - } else if (ext_ctrl->controls->string != NULL) { - dest_buffer = - (u32 *) ext_ctrl->controls->string; - while (index < no_of_scan_freq) { - *(dest_buffer + count + 0) = - HZ_TO_V4L2(scanfreq[index]); - *(dest_buffer + count + 1) = - scanfreq_rssi_level[index]; - count += 2; - index++; - } - ret_val = 0; + } + FM_DEBUG_REPORT("vidioc_get_ext_ctrls: " + "SeekStatus = %x, GlobalEvent = %x, " + "numchannels = %x", + cg2900_device.seekstatus, + fm_event, no_of_scan_freq); + + if (ext_ctrl->controls->size == 0 && + ext_ctrl->controls->string == NULL) { + if (cg2900_device.seekstatus == + FMR_SEEK_IN_PROGRESS && + CG2900_EVENT_SCAN_CHANNELS_FOUND + == fm_event) { + spin_lock(&fm_spinlock); + ext_ctrl->controls->size = + no_of_scan_freq; + cg2900_device.seekstatus + = FMR_SEEK_NONE; + fm_event = + CG2900_EVENT_NO_EVENT; + spin_unlock(&fm_spinlock); + return -ENOSPC; } - break; + } else if (ext_ctrl->controls->string != NULL) { + dest_buffer = + (u32 *) ext_ctrl->controls->string; + while (index < no_of_scan_freq) { + *(dest_buffer + count + 0) = + HZ_TO_V4L2(scanfreq[index]); + *(dest_buffer + count + 1) = + scanfreq_rssi_level[index]; + count += 2; + index++; + } + ret_val = 0; } + break; case V4L2_CID_CG2900_RADIO_BLOCKSCAN_GET_RESULTS: - { - if (ext_ctrl->ctrl_class != V4L2_CTRL_CLASS_USER) { - FM_ERR_REPORT("vidioc_get_ext_ctrls: " - "V4L2_CID_CG2900_RADIO_BLOCKSCAN_GET_RESULTS " - "Unsupported ctrl_class = %04x", - ext_ctrl->ctrl_class); + if (ext_ctrl->ctrl_class != V4L2_CTRL_CLASS_USER) { + FM_ERR_REPORT("vidioc_get_ext_ctrls: " + "V4L2_CID_CG2900_RADIO_BLOCKSCAN" + "_GET_RESULTS " + "Unsupported ctrl_class = %04x", + ext_ctrl->ctrl_class); + break; + } + if (cg2900_device.seekstatus == FMR_SEEK_IN_PROGRESS) { + spin_lock(&fm_spinlock); + skb = skb_dequeue(&fm_interrupt_queue); + spin_unlock(&fm_spinlock); + if (!skb) { + /* No Interrupt, bad case */ + FM_ERR_REPORT("No Interrupt to read"); + fm_event = CG2900_EVENT_NO_EVENT; break; } - if (cg2900_device.seekstatus == - FMR_SEEK_IN_PROGRESS) { - if (fm_event == - CG2900_EVENT_BLOCK_SCAN_CHANNELS_FOUND) { - /* Check to get BlockScan Result */ - status = - cg2900_fm_get_block_scan_result - (&no_of_block_scan_freq, - block_scan_rssi_level); - if (0 != status) { - FM_ERR_REPORT - ("vidioc_get_ext_ctrls: " - "cg2900_fm_get_block_scan_" - "result: " "returned %d", - status); - return ret_val; - } + fm_event = (u8)skb->data[0]; + FM_DEBUG_REPORT( + "V4L2_CID_CG2900_RADIO_BLOCKSCAN" + "GET_RESULTS: " + "fm_event = %x", fm_event); + if (fm_event == + CG2900_EVENT_BLOCK_SCAN_CHANNELS_FOUND) { + /* Check for BlockScan Result */ + status = + cg2900_fm_get_block_scan_result + (&no_of_block_scan_freq, + block_scan_rssi_level); + if (0 != status) { + FM_ERR_REPORT + ("vidioc_get_ext_ctrls: " + "cg2900_fm_get_block_scan_" + "result: returned %d", + status); + kfree_skb(skb); + break; } + kfree_skb(skb); + } else { + /* Some other interrupt, + Queue it back */ + spin_lock(&fm_spinlock); + skb_queue_head(&fm_interrupt_queue, skb); + spin_unlock(&fm_spinlock); } - FM_DEBUG_REPORT("vidioc_get_ext_ctrls: " - "SeekStatus = %d, GlobalEvent = %d, " - "numchannels = %d", - cg2900_device.seekstatus, - fm_event, no_of_block_scan_freq); - if (ext_ctrl->controls->size == 0 && - ext_ctrl->controls->string == NULL) { - if (cg2900_device.seekstatus == - FMR_SEEK_IN_PROGRESS && - CG2900_EVENT_BLOCK_SCAN_CHANNELS_FOUND - == fm_event) { - spin_lock(&fm_spinlock); - ext_ctrl->controls->size = - no_of_block_scan_freq; - cg2900_device.seekstatus - = FMR_SEEK_NONE; - fm_event = - CG2900_EVENT_NO_EVENT; - spin_unlock(&fm_spinlock); - return -ENOSPC; - } - } else if (ext_ctrl->controls->size >= - no_of_block_scan_freq && - ext_ctrl->controls->string != NULL) { - dest_buffer = - (u32 *) ext_ctrl->controls->string; - while (index < no_of_block_scan_freq) { - *(dest_buffer + index) = - block_scan_rssi_level - [index]; - index++; - } - ret_val = 0; - return ret_val; + } + FM_DEBUG_REPORT("vidioc_get_ext_ctrls: " + "SeekStatus = %x, GlobalEvent = %x, " + "numchannels = %x", + cg2900_device.seekstatus, + fm_event, no_of_block_scan_freq); + if (ext_ctrl->controls->size == 0 && + ext_ctrl->controls->string == NULL) { + if (cg2900_device.seekstatus == + FMR_SEEK_IN_PROGRESS && + CG2900_EVENT_BLOCK_SCAN_CHANNELS_FOUND + == fm_event) { + spin_lock(&fm_spinlock); + ext_ctrl->controls->size = + no_of_block_scan_freq; + cg2900_device.seekstatus + = FMR_SEEK_NONE; + fm_event = + CG2900_EVENT_NO_EVENT; + spin_unlock(&fm_spinlock); + return -ENOSPC; } - break; + } else if (ext_ctrl->controls->size >= + no_of_block_scan_freq && + ext_ctrl->controls->string != NULL) { + dest_buffer = + (u32 *) ext_ctrl->controls->string; + while (index < no_of_block_scan_freq) { + *(dest_buffer + index) = + block_scan_rssi_level + [index]; + index++; + } + ret_val = 0; + return ret_val; } + break; case V4L2_CID_RDS_TX_DEVIATION: - { - FM_DEBUG_REPORT("vidioc_get_ext_ctrls: " - "V4L2_CID_RDS_TX_DEVIATION"); - if (V4L2_CTRL_CLASS_FM_TX != ext_ctrl->ctrl_class) { - FM_ERR_REPORT("Invalid Ctrl Class = %d", - ext_ctrl->ctrl_class); - break; - } - status = cg2900_fm_tx_get_rds_deviation((u16 *) & - ext_ctrl-> - controls->value); - if (status == 0) - ret_val = 0; + FM_DEBUG_REPORT("vidioc_get_ext_ctrls: " + "V4L2_CID_RDS_TX_DEVIATION"); + if (V4L2_CTRL_CLASS_FM_TX != ext_ctrl->ctrl_class) { + FM_ERR_REPORT("Invalid Ctrl Class = %x", + ext_ctrl->ctrl_class); break; } + status = cg2900_fm_tx_get_rds_deviation((u16 *) & + ext_ctrl-> + controls->value); + if (status == 0) + ret_val = 0; + break; case V4L2_CID_PILOT_TONE_ENABLED: - { - FM_DEBUG_REPORT("vidioc_get_ext_ctrls: " - "V4L2_CID_PILOT_TONE_ENABLED"); - if (V4L2_CTRL_CLASS_FM_TX != ext_ctrl->ctrl_class) { - FM_ERR_REPORT("Invalid Ctrl Class = %d", - ext_ctrl->ctrl_class); - break; - } - status = cg2900_fm_tx_get_pilot_tone_status( - (bool *)&ext_ctrl->controls->value); - if (status == 0) - ret_val = 0; + FM_DEBUG_REPORT("vidioc_get_ext_ctrls: " + "V4L2_CID_PILOT_TONE_ENABLED"); + if (V4L2_CTRL_CLASS_FM_TX != ext_ctrl->ctrl_class) { + FM_ERR_REPORT("Invalid Ctrl Class = %x", + ext_ctrl->ctrl_class); break; } + status = cg2900_fm_tx_get_pilot_tone_status( + (bool *)&ext_ctrl->controls->value); + if (status == 0) + ret_val = 0; + break; case V4L2_CID_PILOT_TONE_DEVIATION: - { - FM_DEBUG_REPORT("vidioc_get_ext_ctrls: " - "V4L2_CID_PILOT_TONE_DEVIATION"); - if (V4L2_CTRL_CLASS_FM_TX != ext_ctrl->ctrl_class) { - FM_ERR_REPORT("Invalid Ctrl Class = %d", - ext_ctrl->ctrl_class); - break; - } - status = cg2900_fm_tx_get_pilot_deviation( - (u16 *)&ext_ctrl->controls->value); - if (status == 0) - ret_val = 0; + FM_DEBUG_REPORT("vidioc_get_ext_ctrls: " + "V4L2_CID_PILOT_TONE_DEVIATION"); + if (V4L2_CTRL_CLASS_FM_TX != ext_ctrl->ctrl_class) { + FM_ERR_REPORT("Invalid Ctrl Class = %x", + ext_ctrl->ctrl_class); break; } + status = cg2900_fm_tx_get_pilot_deviation( + (u16 *)&ext_ctrl->controls->value); + if (status == 0) + ret_val = 0; + break; case V4L2_CID_TUNE_PREEMPHASIS: - { - FM_DEBUG_REPORT("vidioc_get_ext_ctrls: " - "V4L2_CID_TUNE_PREEMPHASIS"); - if (V4L2_CTRL_CLASS_FM_TX != ext_ctrl->ctrl_class) { - FM_ERR_REPORT("Invalid Ctrl Class = %d", - ext_ctrl->ctrl_class); - break; - } - status = cg2900_fm_tx_get_preemphasis( - (u8 *)&ext_ctrl->controls->value); - if (status == 0) - ret_val = 0; + FM_DEBUG_REPORT("vidioc_get_ext_ctrls: " + "V4L2_CID_TUNE_PREEMPHASIS"); + if (V4L2_CTRL_CLASS_FM_TX != ext_ctrl->ctrl_class) { + FM_ERR_REPORT("Invalid Ctrl Class = %x", + ext_ctrl->ctrl_class); break; } + status = cg2900_fm_tx_get_preemphasis( + (u8 *)&ext_ctrl->controls->value); + if (status == 0) + ret_val = 0; + break; case V4L2_CID_TUNE_POWER_LEVEL: - { - FM_DEBUG_REPORT("vidioc_get_ext_ctrls: " - "V4L2_CID_TUNE_POWER_LEVEL"); - if (V4L2_CTRL_CLASS_FM_TX != ext_ctrl->ctrl_class) { - FM_ERR_REPORT("Invalid Ctrl Class = %d", - ext_ctrl->ctrl_class); - break; - } - status = cg2900_fm_tx_get_power_level( - (u16 *)&ext_ctrl->controls->value); - if (status == 0) - ret_val = 0; + FM_DEBUG_REPORT("vidioc_get_ext_ctrls: " + "V4L2_CID_TUNE_POWER_LEVEL"); + if (V4L2_CTRL_CLASS_FM_TX != ext_ctrl->ctrl_class) { + FM_ERR_REPORT("Invalid Ctrl Class = %x", + ext_ctrl->ctrl_class); break; } - default: - { - FM_DEBUG_REPORT("vidioc_get_ext_ctrls: " - "unsupported (id = %d)", - (int)ext_ctrl->controls->id); + status = cg2900_fm_tx_get_power_level( + (u16 *)&ext_ctrl->controls->value); + if (status == 0) + ret_val = 0; + break; + case V4L2_CID_CG2900_RADIO_GET_INTERRUPT: + if (ext_ctrl->ctrl_class != V4L2_CTRL_CLASS_USER) { + FM_ERR_REPORT("vidioc_get_ext_ctrls: " + "V4L2_CID_CG2900_RADIO_GET_INTERRUPT " + "Unsupported ctrl_class = %04x", + ext_ctrl->ctrl_class); + break; + } + if (ext_ctrl->controls->size != FMR_GET_INTERRUPT_DATA_SIZE || + ext_ctrl->controls->string == NULL) { + FM_ERR_REPORT("vidioc_get_ext_ctrls: " + "V4L2_CID_CG2900_RADIO_GET_INTERRUPT " + "Invalid parameters, ext_ctrl->controls->size = %x " + "ext_ctrl->controls->string = %08x", + ext_ctrl->controls->size, + (unsigned int)ext_ctrl->controls->string); + ret_val = -ENOSPC; + break; + } + spin_lock(&fm_spinlock); + skb = skb_dequeue(&fm_interrupt_queue); + spin_unlock(&fm_spinlock); + if (!skb) { + /* No Interrupt, bad case */ + FM_ERR_REPORT("V4L2_CID_CG2900_RADIO_GET_INTERRUPT: " + "No Interrupt to read"); + fm_event = CG2900_EVENT_NO_EVENT; + break; + } + fm_event = (u8)skb->data[0]; + interrupt_success = (s8)skb->data[1]; + FM_DEBUG_REPORT("vidioc_get_ctrl: Interrupt = %x " + "interrupt_success = %x", + fm_event, interrupt_success); + fm_interrupt_buffer = + (int *) ext_ctrl->controls->string; + /* Interrupt that has occurred */ + *fm_interrupt_buffer = cg2900_map_event_to_v4l2(fm_event); + + /* Interrupt success or failed */ + if (interrupt_success) { + /* Interrupt Success, return 0 */ + *(fm_interrupt_buffer + 1) = 0; + } else { + spin_lock(&fm_spinlock); + no_of_scan_freq = 0; + no_of_block_scan_freq = 0; + spin_unlock(&fm_spinlock); + cg2900_device.seekstatus = FMR_SEEK_NONE; + /* Clear the Interrupt flag */ + fm_event = CG2900_EVENT_NO_EVENT; + kfree_skb(skb); + /* Interrupt Success, return negative error */ + *(fm_interrupt_buffer + 1) = -1; + FM_ERR_REPORT("vidioc_get_ext_ctrls: Interrupt = %d " + "failed with reason = %d", + (*fm_interrupt_buffer), + (*(fm_interrupt_buffer + 1))); + break; + } + + if (CG2900_EVENT_MONO_STEREO_TRANSITION + == fm_event) { + /* + * In case of Mono/Stereo Interrupt, + * get the current value from chip + */ + status = cg2900_fm_get_mode(&mode); + cg2900_device.rx_stereo_status = (bool)mode; + /* Clear the Interrupt flag */ + fm_event = CG2900_EVENT_NO_EVENT; + kfree_skb(skb); + } else if (CG2900_EVENT_SCAN_CANCELLED == + fm_event) { + /* Scan/Search cancelled by User */ + spin_lock(&fm_spinlock); + no_of_scan_freq = 0; + no_of_block_scan_freq = 0; + spin_unlock(&fm_spinlock); + cg2900_device.seekstatus = FMR_SEEK_NONE; + /* Clear the Interrupt flag */ + fm_event = CG2900_EVENT_NO_EVENT; + kfree_skb(skb); + } else { + /* Queue the interrupt back + for later dequeuing */ + FM_DEBUG_REPORT("V4L2_CID_CG2900" + "_RADIO_GET_INTERRUPT: " + "Queuing the interrupt" + "again to head of list"); + spin_lock(&fm_spinlock); + skb_queue_head(&fm_interrupt_queue, skb); + spin_unlock(&fm_spinlock); } + ret_val = 0; + break; + default: + FM_DEBUG_REPORT("vidioc_get_ext_ctrls: " + "unsupported (id = %x)", + ext_ctrl->controls->id); } error: @@ -1622,8 +1780,7 @@ static int vidioc_set_ext_ctrls( FM_DEBUG_REPORT("vidioc_set_ext_ctrls: " "V4L2_CID_CG2900_RADIO_RDS_AF_SWITCH_START: " "AF Switch Freq =%d Hz AF Switch PI = %04x", - af_switch_freq, - af_switch_pi); + (int)af_switch_freq, af_switch_pi); if (af_switch_freq < (FMR_CHINA_LOW_FREQ_IN_MHZ * FMR_HZ_TO_MHZ_CONVERTER) || @@ -1713,7 +1870,7 @@ static int vidioc_set_ext_ctrls( FM_DEBUG_REPORT("vidioc_set_ext_ctrls: " "V4L2_CID_RDS_TX_PS_NAME, " - "PSN = %s, Len = %d", + "PSN = %s, Len = %x", ext_ctrl->controls->string, ext_ctrl->controls->size); @@ -1735,7 +1892,7 @@ static int vidioc_set_ext_ctrls( FM_DEBUG_REPORT("vidioc_set_ext_ctrls: " "V4L2_CID_RDS_TX_RADIO_TEXT, " - "RT = %s, Len = %d", + "RT = %s, Len = %x", ext_ctrl->controls->string, ext_ctrl->controls->size); @@ -1911,8 +2068,8 @@ static int vidioc_set_ext_ctrls( "BLOCKSCAN_START: " "Start Freq = %d Hz " "End Freq = %d Hz", - start_freq, - end_freq); + (int)start_freq, + (int)end_freq); result_freq = end_freq - start_freq; no_of_block_scan_channels = @@ -1922,8 +2079,8 @@ static int vidioc_set_ext_ctrls( if (end_freq < start_freq) { FM_ERR_REPORT("Start Freq (%d Hz) " " > End Freq (%d Hz)", - start_freq, - end_freq); + (int)start_freq, + (int)end_freq); break; } @@ -1931,7 +2088,7 @@ static int vidioc_set_ext_ctrls( (start_freq > high_freq)) { FM_ERR_REPORT("Out of Band Freq: " "Start Freq = %d Hz", - start_freq); + (int)start_freq); break; } @@ -1939,15 +2096,15 @@ static int vidioc_set_ext_ctrls( (end_freq > high_freq)) { FM_ERR_REPORT("Out of Band Freq: " "End Freq = %d Hz", - end_freq); + (int)end_freq); break; } /* Maximum allowed block scan range */ if (FMR_MAX_BLOCK_SCAN_CHANNELS < no_of_block_scan_channels) { - FM_ERR_REPORT("No of channels (%d)" - "exceeds Max Block Scan (%d)", + FM_ERR_REPORT("No of channels (%x)" + "exceeds Max Block Scan (%x)", no_of_block_scan_channels, FMR_MAX_BLOCK_SCAN_CHANNELS); break; @@ -2153,8 +2310,8 @@ static int vidioc_set_hw_freq_seek( FM_INFO_REPORT("vidioc_set_hw_freq_seek"); - FM_DEBUG_REPORT("vidioc_set_hw_freq_seek: Status = %d, " - "Upwards = %d, Wrap Around = %d", + FM_DEBUG_REPORT("vidioc_set_hw_freq_seek: Status = %x, " + "Upwards = %x, Wrap Around = %x", cg2900_device.seekstatus, freq_seek->seek_upward, freq_seek->wrap_around); @@ -2322,6 +2479,39 @@ static void cg2900_convert_err_to_v4l2( } /** + * cg2900_map_event_to_v4l2()- Maps cg2900 event to v4l2 events . + * + * This function maps cg2900 events to corresponding v4l2 events. + * + * @event: This contains the cg2900 event to be converted. + * + * Returns: Corresponding V4L2 events. + */ +static int cg2900_map_event_to_v4l2( + u8 event + ) +{ + switch (event) { + case CG2900_EVENT_MONO_STEREO_TRANSITION: + return V4L2_CG2900_RADIO_INTERRUPT_MONO_STEREO_TRANSITION; + case CG2900_EVENT_SEARCH_CHANNEL_FOUND: + return V4L2_CG2900_RADIO_INTERRUPT_SEARCH_COMPLETED; + case CG2900_EVENT_SCAN_CHANNELS_FOUND: + return V4L2_CG2900_RADIO_INTERRUPT_BAND_SCAN_COMPLETED; + case CG2900_EVENT_BLOCK_SCAN_CHANNELS_FOUND: + return V4L2_CG2900_RADIO_INTERRUPT_BLOCK_SCAN_COMPLETED; + case CG2900_EVENT_SCAN_CANCELLED: + return V4L2_CG2900_RADIO_INTERRUPT_SCAN_CANCELLED; + case CG2900_EVENT_DEVICE_RESET: + return V4L2_CG2900_RADIO_INTERRUPT_DEVICE_RESET; + case CG2900_EVENT_RDS_EVENT: + return V4L2_CG2900_RADIO_INTERRUPT_RDS_RECEIVED; + default: + return V4L2_CG2900_RADIO_INTERRUPT_UNKNOWN; + } +} + +/** * cg2900_open()- This function nitializes and switches on FM. * * This is called when the application opens the character device. @@ -2471,21 +2661,23 @@ static ssize_t cg2900_read( int current_rds_grp; int index = 0; int blocks_to_read; - int ret; struct v4l2_rds_data rdsbuf[MAX_RDS_GROUPS * NUM_OF_RDS_BLOCKS]; struct v4l2_rds_data *rdslocalbuf = rdsbuf; + struct sk_buff *skb; FM_INFO_REPORT("cg2900_read"); blocks_to_read = (count / sizeof(struct v4l2_rds_data)); if (!cg2900_device.rx_rds_enabled) { + /* Remove all Interrupts from the queue */ + skb_queue_purge(&fm_interrupt_queue); FM_INFO_REPORT("cg2900_read: returning 0"); return 0; } if (count % sizeof(struct v4l2_rds_data) != 0) { - FM_ERR_REPORT("cg2900_read: Invalid Number of bytes %d " + FM_ERR_REPORT("cg2900_read: Invalid Number of bytes %x " "requested to read", count); return -EIO; } @@ -2496,44 +2688,6 @@ static ssize_t cg2900_read( return -EAGAIN; } - if (file->f_flags & O_NONBLOCK) { - /* Non blocking mode selected by application */ - if (fm_rds_info.rds_head == fm_rds_info.rds_tail) { - FM_DEBUG_REPORT("cg2900_read: Non Blocking mode " - "selected by application, returning as " - "no RDS data is available"); - return -EAGAIN; - } - } - - if (fm_rds_info.rds_head == fm_rds_info.rds_tail) { - /* - * Blocking mode selected by application - * if data is not available block on read queue - */ - FM_DEBUG_REPORT("cg2900_read: Blocking mode " - "selected by application, waiting till " - "rds data is available"); - cg2900_device.wait_on_read_queue = true; - ret = wait_event_interruptible(cg2900_read_queue, - (fm_rds_info.rds_head != - fm_rds_info.rds_tail)); - cg2900_device.wait_on_read_queue = false; - FM_DEBUG_REPORT("cg2900_read: " - "wait_event_interruptible returned = %d", ret); - if (ret == -ERESTARTSYS) - return -EINTR; - } - - /* - * Check again that in the meantime RDS is not disabled - * by the user, If yes, return. - */ - if (!cg2900_device.rx_rds_enabled) { - FM_INFO_REPORT("cg2900_read: returning 0"); - return 0; - } - current_rds_grp = fm_rds_info.rds_group_sent; if ((fm_rds_info.rds_head == fm_rds_info.rds_tail) || @@ -2614,6 +2768,29 @@ static ssize_t cg2900_read( if (current_rds_grp == MAX_RDS_GROUPS) { fm_rds_info.rds_tail++; current_rds_grp = 0; + /* Dequeue Rds Interrupt here */ + skb = skb_dequeue(&fm_interrupt_queue); + if (!skb) { + /* No Interrupt, bad case */ + FM_ERR_REPORT("cg2900_read: " + "skb is NULL. Major error"); + spin_unlock(&fm_spinlock); + return 0; + } + fm_event = (u8)skb->data[0]; + if (fm_event != CG2900_EVENT_RDS_EVENT) { + /* RDS interrupt not found */ + FM_ERR_REPORT("cg2900_read:" + "RDS interrupt not found" + "for de-queuing." + "fm_event = %x", fm_event); + /* Queue the event back */ + skb_queue_head(&fm_interrupt_queue, + skb); + spin_unlock(&fm_spinlock); + return 0; + } + kfree_skb(skb); } break; default: @@ -2637,13 +2814,12 @@ static ssize_t cg2900_read( if (fm_rds_info.rds_tail == MAX_RDS_BUFFER) fm_rds_info.rds_tail = 0; + spin_unlock(&fm_spinlock); if (copy_to_user(data, rdslocalbuf, count)) { - spin_unlock(&fm_spinlock); FM_ERR_REPORT("cg2900_read: Error " "in copying, returning"); return -EFAULT; } - spin_unlock(&fm_spinlock); return count; } @@ -2651,17 +2827,16 @@ static ssize_t cg2900_read( * cg2900_poll()- Check if the operation is complete or not. * * This function is invoked by application on calling poll() and is used to - * wait till the desired operation seek/Band Scan are complete. - * Driver blocks till the seek/BandScan Operation is complete. The application - * decides to read the results of seek/Band Scan based on the returned value of - * this function. + * wait till the any FM interrupt is received from the chip. + * The application decides to read the corresponding data depending on FM + * interrupt. * * @file: File structure. * @wait: poll table * * Returns: - * POLLRDNORM when Scan Band/Block Scan/Seek station is complete. - * POLLHUP when seek station/Band Scan is stopped by user using Stop Scan. + * POLLRDNORM|POLLIN whenever FM interrupt has occurred. + * 0 whenever the call times out. */ static unsigned int cg2900_poll( struct file *file, @@ -2672,32 +2847,17 @@ static unsigned int cg2900_poll( FM_INFO_REPORT("cg2900_poll"); - poll_wait(file, &cg2900_poll_queue, wait); - - if (cg2900_device.seekstatus == FMR_SEEK_IN_PROGRESS && - ((fm_event == CG2900_EVENT_SCAN_CHANNELS_FOUND) || - (fm_event == CG2900_EVENT_BLOCK_SCAN_CHANNELS_FOUND) || - (fm_event == CG2900_EVENT_SEARCH_CHANNEL_FOUND))) { - /* Scan Completed, send event to application */ - FM_DEBUG_REPORT("poll_wait returning POLLRDNORM"); - ret_val = POLLRDNORM; - goto done; + /* Check if we have some data in queue already */ + if (skb_queue_empty(&fm_interrupt_queue)) { + FM_DEBUG_REPORT("cg2900_poll: Interrupt Queue Empty, waiting"); + /* No Interrupt, wait for it to occur */ + poll_wait(file, &cg2900_poll_queue, wait); } - - if (fm_event == CG2900_EVENT_SCAN_CANCELLED) { - /* Scan/Search cancelled by User */ - spin_lock(&fm_spinlock); - no_of_scan_freq = 0; - no_of_block_scan_freq = 0; - spin_unlock(&fm_spinlock); - cg2900_device.seekstatus = FMR_SEEK_NONE; - fm_event = CG2900_EVENT_NO_EVENT; - FM_DEBUG_REPORT("cg2900_poll: Cancel operation " - "returning POLLHUP"); - FM_DEBUG_REPORT("poll_wait returning POLLHUP"); - ret_val = POLLHUP; + /* Check if we now have interrupt to read in queue */ + if (skb_queue_empty(&fm_interrupt_queue)) goto done; - } + + ret_val = POLLIN | POLLRDNORM; done: FM_DEBUG_REPORT("poll_wait returning %d", ret_val); @@ -2747,6 +2907,7 @@ static int __devinit radio_cg2900_probe( mutex_init(&fm_mutex); spin_lock_init(&fm_spinlock); init_waitqueue_head(&cg2900_poll_queue); + skb_queue_head_init(&fm_interrupt_queue); users = 0; return 0; } @@ -2767,10 +2928,15 @@ static int __devexit radio_cg2900_remove( ) { FM_INFO_REPORT("radio_cg2900_remove"); + /* Wake up the poll queue since we are now exiting */ + wake_up_poll_queue(); + /* Give some time for application to exit the poll thread */ + schedule_timeout_interruptible(msecs_to_jiffies(500)); /* Try to Switch Off FM in case it is still switched on */ cg2900_fm_switch_off(); cg2900_fm_deinit(); + skb_queue_purge(&fm_interrupt_queue); mutex_destroy(&fm_mutex); video_unregister_device(&cg2900_video_device); fmd_set_dev(NULL); @@ -2814,24 +2980,20 @@ void wake_up_poll_queue(void) wake_up_interruptible(&cg2900_poll_queue); } -void wake_up_read_queue(void) -{ - FM_INFO_REPORT("wake_up_read_queue"); - if (cg2900_device.wait_on_read_queue) - wake_up_interruptible(&cg2900_read_queue); -} - void cg2900_handle_device_reset(void) { + struct sk_buff *skb; FM_INFO_REPORT("cg2900_handle_device_reset"); - users = 0; - cg2900_device.state = FMR_SWITCH_OFF; - cg2900_device.frequency = 0; - cg2900_device.rx_rds_enabled = false; - cg2900_device.muted = false; - cg2900_device.seekstatus = FMR_SEEK_NONE; - fm_event = CG2900_EVENT_NO_EVENT; - no_of_scan_freq = 0; + skb = alloc_skb(SKB_FM_INTERRUPT_DATA, GFP_KERNEL); + if (!skb) { + FM_ERR_REPORT("cg2900_handle_device_reset: " + "Unable to Allocate Memory"); + return; + } + skb->data[0] = CG2900_EVENT_DEVICE_RESET; + skb->data[1] = true; + skb_queue_tail(&fm_interrupt_queue, skb); + wake_up_poll_queue(); } module_init(radio_cg2900_init); |
