summaryrefslogtreecommitdiff
path: root/drivers/media/radio/CG2900/radio-cg2900.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/media/radio/CG2900/radio-cg2900.c')
-rw-r--r--drivers/media/radio/CG2900/radio-cg2900.c756
1 files changed, 459 insertions, 297 deletions
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);