cregit-Linux how code gets into the kernel

Release 4.11 drivers/staging/media/bcm2048/radio-bcm2048.c

/*
 * drivers/staging/media/radio-bcm2048.c
 *
 * Driver for I2C Broadcom BCM2048 FM Radio Receiver:
 *
 * Copyright (C) Nokia Corporation
 * Contact: Eero Nurkkala <ext-eero.nurkkala@nokia.com>
 *
 * Copyright (C) Nils Faerber <nils.faerber@kernelconcepts.de>
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * version 2 as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 */

/*
 * History:
 *              Eero Nurkkala <ext-eero.nurkkala@nokia.com>
 *              Version 0.0.1
 *              - Initial implementation
 * 2010-02-21   Nils Faerber <nils.faerber@kernelconcepts.de>
 *              Version 0.0.2
 *              - Add support for interrupt driven rds data reading
 */

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/version.h>
#include <linux/interrupt.h>
#include <linux/sysfs.h>
#include <linux/completion.h>
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/videodev2.h>
#include <linux/mutex.h>
#include <linux/slab.h>
#include <media/v4l2-common.h>
#include <media/v4l2-ioctl.h>
#include "radio-bcm2048.h"

/* driver definitions */

#define BCM2048_DRIVER_AUTHOR	"Eero Nurkkala <ext-eero.nurkkala@nokia.com>"

#define BCM2048_DRIVER_NAME	BCM2048_NAME

#define BCM2048_DRIVER_VERSION	KERNEL_VERSION(0, 0, 1)

#define BCM2048_DRIVER_CARD	"Broadcom bcm2048 FM Radio Receiver"

#define BCM2048_DRIVER_DESC	"I2C driver for BCM2048 FM Radio Receiver"

/* I2C Control Registers */

#define BCM2048_I2C_FM_RDS_SYSTEM	0x00

#define BCM2048_I2C_FM_CTRL		0x01

#define BCM2048_I2C_RDS_CTRL0		0x02

#define BCM2048_I2C_RDS_CTRL1		0x03

#define BCM2048_I2C_FM_AUDIO_PAUSE	0x04

#define BCM2048_I2C_FM_AUDIO_CTRL0	0x05

#define BCM2048_I2C_FM_AUDIO_CTRL1	0x06

#define BCM2048_I2C_FM_SEARCH_CTRL0	0x07

#define BCM2048_I2C_FM_SEARCH_CTRL1	0x08

#define BCM2048_I2C_FM_SEARCH_TUNE_MODE	0x09

#define BCM2048_I2C_FM_FREQ0		0x0a

#define BCM2048_I2C_FM_FREQ1		0x0b

#define BCM2048_I2C_FM_AF_FREQ0		0x0c

#define BCM2048_I2C_FM_AF_FREQ1		0x0d

#define BCM2048_I2C_FM_CARRIER		0x0e

#define BCM2048_I2C_FM_RSSI		0x0f

#define BCM2048_I2C_FM_RDS_MASK0	0x10

#define BCM2048_I2C_FM_RDS_MASK1	0x11

#define BCM2048_I2C_FM_RDS_FLAG0	0x12

#define BCM2048_I2C_FM_RDS_FLAG1	0x13

#define BCM2048_I2C_RDS_WLINE		0x14

#define BCM2048_I2C_RDS_BLKB_MATCH0	0x16

#define BCM2048_I2C_RDS_BLKB_MATCH1	0x17

#define BCM2048_I2C_RDS_BLKB_MASK0	0x18

#define BCM2048_I2C_RDS_BLKB_MASK1	0x19

#define BCM2048_I2C_RDS_PI_MATCH0	0x1a

#define BCM2048_I2C_RDS_PI_MATCH1	0x1b

#define BCM2048_I2C_RDS_PI_MASK0	0x1c

#define BCM2048_I2C_RDS_PI_MASK1	0x1d

#define BCM2048_I2C_SPARE1		0x20

#define BCM2048_I2C_SPARE2		0x21

#define BCM2048_I2C_FM_RDS_REV		0x28

#define BCM2048_I2C_SLAVE_CONFIGURATION	0x29

#define BCM2048_I2C_RDS_DATA		0x80

#define BCM2048_I2C_FM_BEST_TUNE_MODE	0x90

/* BCM2048_I2C_FM_RDS_SYSTEM */

#define BCM2048_FM_ON			0x01

#define BCM2048_RDS_ON			0x02

/* BCM2048_I2C_FM_CTRL */

#define BCM2048_BAND_SELECT			0x01

#define BCM2048_STEREO_MONO_AUTO_SELECT		0x02

#define BCM2048_STEREO_MONO_MANUAL_SELECT	0x04

#define BCM2048_STEREO_MONO_BLEND_SWITCH	0x08

#define BCM2048_HI_LO_INJECTION			0x10

/* BCM2048_I2C_RDS_CTRL0 */

#define BCM2048_RBDS_RDS_SELECT		0x01

#define BCM2048_FLUSH_FIFO		0x02

/* BCM2048_I2C_FM_AUDIO_PAUSE */

#define BCM2048_AUDIO_PAUSE_RSSI_TRESH	0x0f

#define BCM2048_AUDIO_PAUSE_DURATION	0xf0

/* BCM2048_I2C_FM_AUDIO_CTRL0 */

#define BCM2048_RF_MUTE			0x01

#define BCM2048_MANUAL_MUTE		0x02

#define BCM2048_DAC_OUTPUT_LEFT		0x04

#define BCM2048_DAC_OUTPUT_RIGHT	0x08

#define BCM2048_AUDIO_ROUTE_DAC		0x10

#define BCM2048_AUDIO_ROUTE_I2S		0x20

#define BCM2048_DE_EMPHASIS_SELECT	0x40

#define BCM2048_AUDIO_BANDWIDTH_SELECT	0x80

/* BCM2048_I2C_FM_SEARCH_CTRL0 */

#define BCM2048_SEARCH_RSSI_THRESHOLD	0x7f

#define BCM2048_SEARCH_DIRECTION	0x80

/* BCM2048_I2C_FM_SEARCH_TUNE_MODE */

#define BCM2048_FM_AUTO_SEARCH		0x03

/* BCM2048_I2C_FM_RSSI */

#define BCM2048_RSSI_VALUE		0xff

/* BCM2048_I2C_FM_RDS_MASK0 */
/* BCM2048_I2C_FM_RDS_MASK1 */

#define BCM2048_FM_FLAG_SEARCH_TUNE_FINISHED	0x01

#define BCM2048_FM_FLAG_SEARCH_TUNE_FAIL	0x02

#define BCM2048_FM_FLAG_RSSI_LOW		0x04

#define BCM2048_FM_FLAG_CARRIER_ERROR_HIGH	0x08

#define BCM2048_FM_FLAG_AUDIO_PAUSE_INDICATION	0x10

#define BCM2048_FLAG_STEREO_DETECTED		0x20

#define BCM2048_FLAG_STEREO_ACTIVE		0x40

/* BCM2048_I2C_RDS_DATA */

#define BCM2048_SLAVE_ADDRESS			0x3f

#define BCM2048_SLAVE_ENABLE			0x80

/* BCM2048_I2C_FM_BEST_TUNE_MODE */

#define BCM2048_BEST_TUNE_MODE			0x80


#define BCM2048_FM_FLAG_SEARCH_TUNE_FINISHED	0x01

#define BCM2048_FM_FLAG_SEARCH_TUNE_FAIL	0x02

#define BCM2048_FM_FLAG_RSSI_LOW		0x04

#define BCM2048_FM_FLAG_CARRIER_ERROR_HIGH	0x08

#define BCM2048_FM_FLAG_AUDIO_PAUSE_INDICATION	0x10

#define BCM2048_FLAG_STEREO_DETECTED		0x20

#define BCM2048_FLAG_STEREO_ACTIVE		0x40


#define BCM2048_RDS_FLAG_FIFO_WLINE		0x02

#define BCM2048_RDS_FLAG_B_BLOCK_MATCH		0x08

#define BCM2048_RDS_FLAG_SYNC_LOST		0x10

#define BCM2048_RDS_FLAG_PI_MATCH		0x20


#define BCM2048_RDS_MARK_END_BYTE0		0x7C

#define BCM2048_RDS_MARK_END_BYTEN		0xFF


#define BCM2048_FM_FLAGS_ALL	(FM_FLAG_SEARCH_TUNE_FINISHED | \
                                 FM_FLAG_SEARCH_TUNE_FAIL | \
                                 FM_FLAG_RSSI_LOW | \
                                 FM_FLAG_CARRIER_ERROR_HIGH | \
                                 FM_FLAG_AUDIO_PAUSE_INDICATION | \
                                 FLAG_STEREO_DETECTED | FLAG_STEREO_ACTIVE)


#define BCM2048_RDS_FLAGS_ALL	(RDS_FLAG_FIFO_WLINE | \
                                 RDS_FLAG_B_BLOCK_MATCH | \
                                 RDS_FLAG_SYNC_LOST | RDS_FLAG_PI_MATCH)


#define BCM2048_DEFAULT_TIMEOUT		1500

#define BCM2048_AUTO_SEARCH_TIMEOUT	3000


#define BCM2048_FREQDEV_UNIT		10000

#define BCM2048_FREQV4L2_MULTI		625

#define dev_to_v4l2(f)	((f * BCM2048_FREQDEV_UNIT) / BCM2048_FREQV4L2_MULTI)

#define v4l2_to_dev(f)	((f * BCM2048_FREQV4L2_MULTI) / BCM2048_FREQDEV_UNIT)


#define msb(x)                  ((u8)((u16)x >> 8))

#define lsb(x)                  ((u8)((u16)x &  0x00FF))

#define compose_u16(msb, lsb)	(((u16)msb << 8) | lsb)


#define BCM2048_DEFAULT_POWERING_DELAY	20

#define BCM2048_DEFAULT_REGION		0x02

#define BCM2048_DEFAULT_MUTE		0x01

#define BCM2048_DEFAULT_RSSI_THRESHOLD	0x64

#define BCM2048_DEFAULT_RDS_WLINE	0x7E


#define BCM2048_FM_SEARCH_INACTIVE	0x00

#define BCM2048_FM_PRE_SET_MODE		0x01

#define BCM2048_FM_AUTO_SEARCH_MODE	0x02

#define BCM2048_FM_AF_JUMP_MODE		0x03


#define BCM2048_FREQUENCY_BASE		64000


#define BCM2048_POWER_ON		0x01

#define BCM2048_POWER_OFF		0x00


#define BCM2048_ITEM_ENABLED		0x01

#define BCM2048_SEARCH_DIRECTION_UP	0x01


#define BCM2048_DE_EMPHASIS_75us	75

#define BCM2048_DE_EMPHASIS_50us	50


#define BCM2048_SCAN_FAIL		0x00

#define BCM2048_SCAN_OK			0x01


#define BCM2048_FREQ_ERROR_FLOOR	-20

#define BCM2048_FREQ_ERROR_ROOF		20

/* -60 dB is reported as full signal strength */

#define BCM2048_RSSI_LEVEL_BASE		-60

#define BCM2048_RSSI_LEVEL_ROOF		-100

#define BCM2048_RSSI_LEVEL_ROOF_NEG	100

#define BCM2048_SIGNAL_MULTIPLIER	(0xFFFF / \
                                         (BCM2048_RSSI_LEVEL_ROOF_NEG + \
                                          BCM2048_RSSI_LEVEL_BASE))


#define BCM2048_RDS_FIFO_DUPLE_SIZE	0x03

#define BCM2048_RDS_CRC_MASK		0x0F

#define BCM2048_RDS_CRC_NONE		0x00

#define BCM2048_RDS_CRC_MAX_2BITS	0x04

#define BCM2048_RDS_CRC_LEAST_2BITS	0x08

#define BCM2048_RDS_CRC_UNRECOVARABLE	0x0C


#define BCM2048_RDS_BLOCK_MASK		0xF0

#define BCM2048_RDS_BLOCK_A		0x00

#define BCM2048_RDS_BLOCK_B		0x10

#define BCM2048_RDS_BLOCK_C		0x20

#define BCM2048_RDS_BLOCK_D		0x30

#define BCM2048_RDS_BLOCK_C_SCORED	0x40

#define BCM2048_RDS_BLOCK_E		0x60


#define BCM2048_RDS_RT			0x20

#define BCM2048_RDS_PS			0x00


#define BCM2048_RDS_GROUP_AB_MASK	0x08

#define BCM2048_RDS_GROUP_A		0x00

#define BCM2048_RDS_GROUP_B		0x08


#define BCM2048_RDS_RT_AB_MASK		0x10

#define BCM2048_RDS_RT_A		0x00

#define BCM2048_RDS_RT_B		0x10

#define BCM2048_RDS_RT_INDEX		0x0F


#define BCM2048_RDS_PS_INDEX		0x03


struct rds_info {
	
u16 rds_pi;

#define BCM2048_MAX_RDS_RT (64 + 1)
	
u8 rds_rt[BCM2048_MAX_RDS_RT];
	
u8 rds_rt_group_b;
	
u8 rds_rt_ab;

#define BCM2048_MAX_RDS_PS (8 + 1)
	
u8 rds_ps[BCM2048_MAX_RDS_PS];
	
u8 rds_ps_group;
	
u8 rds_ps_group_cnt;

#define BCM2048_MAX_RDS_RADIO_TEXT 255
	
u8 radio_text[BCM2048_MAX_RDS_RADIO_TEXT + 3];
	
u8 text_len;
};


struct region_info {
	
u32 bottom_frequency;
	
u32 top_frequency;
	
u8 deemphasis;
	
u8 channel_spacing;
	
u8 region;
};


struct bcm2048_device {
	
struct i2c_client *client;
	
struct video_device videodev;
	
struct work_struct work;
	
struct completion compl;
	
struct mutex mutex;
	
struct bcm2048_platform_data *platform_data;
	
struct rds_info rds_info;
	
struct region_info region_info;
	
u16 frequency;
	
u8 cache_fm_rds_system;
	
u8 cache_fm_ctrl;
	
u8 cache_fm_audio_ctrl0;
	
u8 cache_fm_search_ctrl0;
	
u8 power_state;
	
u8 rds_state;
	
u8 fifo_size;
	
u8 scan_state;
	
u8 mute_state;

	/* for rds data device read */
	
wait_queue_head_t read_queue;
	
unsigned int users;
	
unsigned char rds_data_available;
	
unsigned int rd_index;
};


static int radio_nr = -1;	
/* radio device minor (-1 ==> auto assign) */
module_param(radio_nr, int, 0000);
MODULE_PARM_DESC(radio_nr,
		 "Minor number for radio device (-1 ==> auto assign)");


static const struct region_info region_configs[] = {
	/* USA */
	{
		.channel_spacing	= 20,
		.bottom_frequency	= 87500,
		.top_frequency		= 108000,
		.deemphasis		= 75,
		.region			= 0,
        },
	/* Australia */
	{
		.channel_spacing	= 20,
		.bottom_frequency	= 87500,
		.top_frequency		= 108000,
		.deemphasis		= 50,
		.region			= 1,
        },
	/* Europe */
	{
		.channel_spacing	= 10,
		.bottom_frequency	= 87500,
		.top_frequency		= 108000,
		.deemphasis		= 50,
		.region			= 2,
        },
	/* Japan */
	{
		.channel_spacing	= 10,
		.bottom_frequency	= 76000,
		.top_frequency		= 90000,
		.deemphasis		= 50,
		.region			= 3,
        },
};

/*
 *      I2C Interface read / write
 */

static int bcm2048_send_command(struct bcm2048_device *bdev, unsigned int reg, unsigned int value) { struct i2c_client *client = bdev->client; u8 data[2]; if (!bdev->power_state) { dev_err(&bdev->client->dev, "bcm2048: chip not powered!\n"); return -EIO; } data[0] = reg & 0xff; data[1] = value & 0xff; if (i2c_master_send(client, data, 2) == 2) return 0; dev_err(&bdev->client->dev, "BCM I2C error!\n"); dev_err(&bdev->client->dev, "Is Bluetooth up and running?\n"); return -EIO; }

Contributors

PersonTokensPropCommitsCommitProp
Hans Verkuil121100.00%1100.00%
Total121100.00%1100.00%


static int bcm2048_recv_command(struct bcm2048_device *bdev, unsigned int reg, u8 *value) { struct i2c_client *client = bdev->client; if (!bdev->power_state) { dev_err(&bdev->client->dev, "bcm2048: chip not powered!\n"); return -EIO; } value[0] = i2c_smbus_read_byte_data(client, reg & 0xff); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Hans Verkuil70100.00%1100.00%
Total70100.00%1100.00%


static int bcm2048_recv_duples(struct bcm2048_device *bdev, unsigned int reg, u8 *value, u8 duples) { struct i2c_client *client = bdev->client; struct i2c_adapter *adap = client->adapter; struct i2c_msg msg[2]; u8 buf; if (!bdev->power_state) { dev_err(&bdev->client->dev, "bcm2048: chip not powered!\n"); return -EIO; } buf = reg & 0xff; msg[0].addr = client->addr; msg[0].flags = client->flags & I2C_M_TEN; msg[0].len = 1; msg[0].buf = &buf; msg[1].addr = client->addr; msg[1].flags = client->flags & I2C_M_TEN; msg[1].flags |= I2C_M_RD; msg[1].len = duples; msg[1].buf = value; return i2c_transfer(adap, msg, 2); }

Contributors

PersonTokensPropCommitsCommitProp
Hans Verkuil185100.00%1100.00%
Total185100.00%1100.00%

/* * BCM2048 - I2C register programming helpers */
static int bcm2048_set_power_state(struct bcm2048_device *bdev, u8 power) { int err = 0; mutex_lock(&bdev->mutex); if (power) { bdev->power_state = BCM2048_POWER_ON; bdev->cache_fm_rds_system |= BCM2048_FM_ON; } else { bdev->cache_fm_rds_system &= ~BCM2048_FM_ON; } /* * Warning! FM cannot be turned off because then * the I2C communications get ruined! * Comment off the "if (power)" when the chip works! */ if (power) err = bcm2048_send_command(bdev, BCM2048_I2C_FM_RDS_SYSTEM, bdev->cache_fm_rds_system); msleep(BCM2048_DEFAULT_POWERING_DELAY); if (!power) bdev->power_state = BCM2048_POWER_OFF; mutex_unlock(&bdev->mutex); return err; }

Contributors

PersonTokensPropCommitsCommitProp
Hans Verkuil100100.00%1100.00%
Total100100.00%1100.00%


static int bcm2048_get_power_state(struct bcm2048_device *bdev) { int err; u8 value; mutex_lock(&bdev->mutex); err = bcm2048_recv_command(bdev, BCM2048_I2C_FM_RDS_SYSTEM, &value); mutex_unlock(&bdev->mutex); if (!err && (value & BCM2048_FM_ON)) return BCM2048_POWER_ON; return err; }

Contributors

PersonTokensPropCommitsCommitProp
Hans Verkuil62100.00%1100.00%
Total62100.00%1100.00%


static int bcm2048_set_rds_no_lock(struct bcm2048_device *bdev, u8 rds_on) { int err; u8 flags; bdev->cache_fm_rds_system &= ~BCM2048_RDS_ON; if (rds_on) { bdev->cache_fm_rds_system |= BCM2048_RDS_ON; bdev->rds_state = BCM2048_RDS_ON; flags = BCM2048_RDS_FLAG_FIFO_WLINE; err = bcm2048_send_command(bdev, BCM2048_I2C_FM_RDS_MASK1, flags); } else { flags = 0; bdev->rds_state = 0; err = bcm2048_send_command(bdev, BCM2048_I2C_FM_RDS_MASK1, flags); memset(&bdev->rds_info, 0, sizeof(bdev->rds_info)); } if (err) return err; return bcm2048_send_command(bdev, BCM2048_I2C_FM_RDS_SYSTEM, bdev->cache_fm_rds_system); }

Contributors

PersonTokensPropCommitsCommitProp
Hans Verkuil11293.33%133.33%
Mauro Carvalho Chehab75.83%133.33%
Rehas Sachdeva10.83%133.33%
Total120100.00%3100.00%


static int bcm2048_get_rds_no_lock(struct bcm2048_device *bdev) { int err; u8 value; err = bcm2048_recv_command(bdev, BCM2048_I2C_FM_RDS_SYSTEM, &value); if (!err && (value & BCM2048_RDS_ON)) return BCM2048_ITEM_ENABLED; return err; }

Contributors

PersonTokensPropCommitsCommitProp
Hans Verkuil46100.00%1100.00%
Total46100.00%1100.00%


static int bcm2048_set_rds(struct bcm2048_device *bdev, u8 rds_on) { int err; mutex_lock(&bdev->mutex); err = bcm2048_set_rds_no_lock(bdev, rds_on); mutex_unlock(&bdev->mutex); return err; }

Contributors

PersonTokensPropCommitsCommitProp
Hans Verkuil45100.00%1100.00%
Total45100.00%1100.00%


static int bcm2048_get_rds(struct bcm2048_device *bdev) { int err; mutex_lock(&bdev->mutex); err = bcm2048_get_rds_no_lock(bdev); mutex_unlock(&bdev->mutex); return err; }

Contributors

PersonTokensPropCommitsCommitProp
Hans Verkuil40100.00%1100.00%
Total40100.00%1100.00%


static int bcm2048_get_rds_pi(struct bcm2048_device *bdev) { return bdev->rds_info.rds_pi; }

Contributors

PersonTokensPropCommitsCommitProp
Hans Verkuil18100.00%1100.00%
Total18100.00%1100.00%


static int bcm2048_set_fm_automatic_stereo_mono(struct bcm2048_device *bdev, u8 enabled) { int err; mutex_lock(&bdev->mutex); bdev->cache_fm_ctrl &= ~BCM2048_STEREO_MONO_AUTO_SELECT; if (enabled) bdev->cache_fm_ctrl |= BCM2048_STEREO_MONO_AUTO_SELECT; err = bcm2048_send_command(bdev, BCM2048_I2C_FM_CTRL, bdev->cache_fm_ctrl); mutex_unlock(&bdev->mutex); return err; }

Contributors

PersonTokensPropCommitsCommitProp
Hans Verkuil66100.00%1100.00%
Total66100.00%1100.00%


static int bcm2048_set_fm_hi_lo_injection(struct bcm2048_device *bdev, u8 hi_lo) { int err; mutex_lock(&bdev->mutex); bdev->cache_fm_ctrl &= ~BCM2048_HI_LO_INJECTION; if (hi_lo) bdev->cache_fm_ctrl |= BCM2048_HI_LO_INJECTION; err = bcm2048_send_command(bdev, BCM2048_I2C_FM_CTRL, bdev->cache_fm_ctrl); mutex_unlock(&bdev->mutex); return err; }

Contributors

PersonTokensPropCommitsCommitProp
Hans Verkuil66100.00%1100.00%
Total66100.00%1100.00%


static int bcm2048_get_fm_hi_lo_injection(struct bcm2048_device *bdev) { int err; u8 value; mutex_lock(&bdev->mutex); err = bcm2048_recv_command(bdev, BCM2048_I2C_FM_CTRL, &value); mutex_unlock(&bdev->mutex); if (!err && (value & BCM2048_HI_LO_INJECTION)) return BCM2048_ITEM_ENABLED; return err; }

Contributors

PersonTokensPropCommitsCommitProp
Hans Verkuil62100.00%1100.00%
Total62100.00%1100.00%


static int bcm2048_set_fm_frequency(struct bcm2048_device *bdev, u32 frequency) { int err; if (frequency < bdev->region_info.bottom_frequency || frequency > bdev->region_info.top_frequency) return -EDOM; frequency -= BCM2048_FREQUENCY_BASE; mutex_lock(&bdev->mutex); err = bcm2048_send_command(bdev, BCM2048_I2C_FM_FREQ0, lsb(frequency)); err |= bcm2048_send_command(bdev, BCM2048_I2C_FM_FREQ1, msb(frequency)); if (!err) bdev->frequency = frequency; mutex_unlock(&bdev->mutex); return err; }

Contributors

PersonTokensPropCommitsCommitProp
Hans Verkuil101100.00%1100.00%
Total101100.00%1100.00%


static int bcm2048_get_fm_frequency(struct bcm2048_device *bdev) { int err; u8 lsb = 0, msb = 0; mutex_lock(&bdev->mutex); err = bcm2048_recv_command(bdev, BCM2048_I2C_FM_FREQ0, &lsb); err |= bcm2048_recv_command(bdev, BCM2048_I2C_FM_FREQ1, &msb); mutex_unlock(&bdev->mutex); if (err) return err; err = compose_u16(msb, lsb); err += BCM2048_FREQUENCY_BASE; return err; }

Contributors

PersonTokensPropCommitsCommitProp
Hans Verkuil8295.35%150.00%
Andrew Milkovich44.65%150.00%
Total86100.00%2100.00%


static int bcm2048_set_fm_af_frequency(struct bcm2048_device *bdev, u32 frequency) { int err; if (frequency < bdev->region_info.bottom_frequency || frequency > bdev->region_info.top_frequency) return -EDOM; frequency -= BCM2048_FREQUENCY_BASE; mutex_lock(&bdev->mutex); err = bcm2048_send_command(bdev, BCM2048_I2C_FM_AF_FREQ0, lsb(frequency)); err |= bcm2048_send_command(bdev, BCM2048_I2C_FM_AF_FREQ1, msb(frequency)); if (!err) bdev->frequency = frequency; mutex_unlock(&bdev->mutex); return err; }

Contributors

PersonTokensPropCommitsCommitProp
Hans Verkuil101100.00%1100.00%
Total101100.00%1100.00%


static int bcm2048_get_fm_af_frequency(struct bcm2048_device *bdev) { int err; u8 lsb = 0, msb = 0; mutex_lock(&bdev->mutex); err = bcm2048_recv_command(bdev, BCM2048_I2C_FM_AF_FREQ0, &lsb); err |= bcm2048_recv_command(bdev, BCM2048_I2C_FM_AF_FREQ1, &msb); mutex_unlock(&bdev->mutex); if (err) return err; err = compose_u16(msb, lsb); err += BCM2048_FREQUENCY_BASE; return err; }

Contributors

PersonTokensPropCommitsCommitProp
Hans Verkuil8295.35%150.00%
Andrew Milkovich44.65%150.00%
Total86100.00%2100.00%


static int bcm2048_set_fm_deemphasis(struct bcm2048_device *bdev, int d) { int err; u8 deemphasis; if (d == BCM2048_DE_EMPHASIS_75us) deemphasis = BCM2048_DE_EMPHASIS_SELECT; else deemphasis = 0; mutex_lock(&bdev->mutex); bdev->cache_fm_audio_ctrl0 &= ~BCM2048_DE_EMPHASIS_SELECT; bdev->cache_fm_audio_ctrl0 |= deemphasis; err = bcm2048_send_command(bdev, BCM2048_I2C_FM_AUDIO_CTRL0, bdev->cache_fm_audio_ctrl0); if (!err) bdev->region_info.deemphasis = d; mutex_unlock(&bdev->mutex); return err; }

Contributors

PersonTokensPropCommitsCommitProp
Hans Verkuil93100.00%1100.00%
Total93100.00%1100.00%


static int bcm2048_get_fm_deemphasis(struct bcm2048_device *bdev) { int err; u8 value; mutex_lock(&bdev->mutex); err = bcm2048_recv_command(bdev, BCM2048_I2C_FM_AUDIO_CTRL0, &value); mutex_unlock(&bdev->mutex); if (!err) { if (value & BCM2048_DE_EMPHASIS_SELECT) return BCM2048_DE_EMPHASIS_75us; return BCM2048_DE_EMPHASIS_50us; } return err; }

Contributors

PersonTokensPropCommitsCommitProp
Hans Verkuil67100.00%1100.00%
Total67100.00%1100.00%


static int bcm2048_set_region(struct bcm2048_device *bdev, u8 region) { int err; u32 new_frequency = 0; if (region >= ARRAY_SIZE(region_configs)) return -EINVAL; mutex_lock(&bdev->mutex); bdev->region_info = region_configs[region]; if (region_configs[region].bottom_frequency < 87500) bdev->cache_fm_ctrl |= BCM2048_BAND_SELECT; else bdev->cache_fm_ctrl &= ~BCM2048_BAND_SELECT; err = bcm2048_send_command(bdev, BCM2048_I2C_FM_CTRL, bdev->cache_fm_ctrl); if (err) { mutex_unlock(&bdev->mutex); goto done; } mutex_unlock(&bdev->mutex); if (bdev->frequency < region_configs[region].bottom_frequency || bdev->frequency > region_configs[region].top_frequency) new_frequency = region_configs[region].bottom_frequency; if (new_frequency > 0) { err = bcm2048_set_fm_frequency(bdev, new_frequency); if (err) goto done; } err = bcm2048_set_fm_deemphasis(bdev, region_configs[region].deemphasis); done: return err; }

Contributors

PersonTokensPropCommitsCommitProp
Hans Verkuil13470.16%125.00%
Jan Roemisch5528.80%125.00%
Pali Rohár10.52%125.00%
Fengguang Wu10.52%125.00%
Total191100.00%4100.00%


static int bcm2048_get_region(struct bcm2048_device *bdev) { int err; mutex_lock(&bdev->mutex); err = bdev->region_info.region; mutex_unlock(&bdev->mutex); return err; }

Contributors

PersonTokensPropCommitsCommitProp
Hans Verkuil41100.00%1100.00%
Total41100.00%1100.00%


static int bcm2048_set_mute(struct bcm2048_device *bdev, u16 mute) { int err; mutex_lock(&bdev->mutex); bdev->cache_fm_audio_ctrl0 &= ~(BCM2048_RF_MUTE | BCM2048_MANUAL_MUTE); if (mute) bdev->cache_fm_audio_ctrl0 |= (BCM2048_RF_MUTE | BCM2048_MANUAL_MUTE); err = bcm2048_send_command(bdev, BCM2048_I2C_FM_AUDIO_CTRL0, bdev->cache_fm_audio_ctrl0); if (!err) bdev->mute_state = mute; mutex_unlock(&bdev->mutex); return err; }

Contributors

PersonTokensPropCommitsCommitProp
Hans Verkuil85100.00%1100.00%
Total85100.00%1100.00%


static int bcm2048_get_mute(struct bcm2048_device *bdev) { int err; u8 value; mutex_lock(&bdev->mutex); if (bdev->power_state) { err = bcm2048_recv_command(bdev, BCM2048_I2C_FM_AUDIO_CTRL0, &value); if (!err) err = value & (BCM2048_RF_MUTE | BCM2048_MANUAL_MUTE); } else { err = bdev->mute_state; } mutex_unlock(&bdev->mutex); return err; }

Contributors

PersonTokensPropCommitsCommitProp
Hans Verkuil80100.00%1100.00%
Total80100.00%1100.00%


static int bcm2048_set_audio_route(struct bcm2048_device *bdev, u8 route) { int err; mutex_lock(&bdev->mutex); route &= (BCM2048_AUDIO_ROUTE_DAC | BCM2048_AUDIO_ROUTE_I2S); bdev->cache_fm_audio_ctrl0 &= ~(BCM2048_AUDIO_ROUTE_DAC | BCM2048_AUDIO_ROUTE_I2S); bdev->cache_fm_audio_ctrl0 |= route; err = bcm2048_send_command(bdev, BCM2048_I2C_FM_AUDIO_CTRL0, bdev->cache_fm_audio_ctrl0); mutex_unlock(&bdev->mutex); return err; }

Contributors

PersonTokensPropCommitsCommitProp
Hans Verkuil74100.00%1100.00%
Total74100.00%1100.00%


static int bcm2048_get_audio_route(struct bcm2048_device *bdev) { int err; u8 value; mutex_lock(&bdev->mutex); err = bcm2048_recv_command(bdev, BCM2048_I2C_FM_AUDIO_CTRL0, &value); mutex_unlock(&bdev->mutex); if (!err) return value & (BCM2048_AUDIO_ROUTE_DAC | BCM2048_AUDIO_ROUTE_I2S); return err; }

Contributors

PersonTokensPropCommitsCommitProp
Hans Verkuil62100.00%1100.00%
Total62100.00%1100.00%


static int bcm2048_set_dac_output(struct bcm2048_device *bdev, u8 channels) { int err; mutex_lock(&bdev->mutex); bdev->cache_fm_audio_ctrl0 &= ~(BCM2048_DAC_OUTPUT_LEFT | BCM2048_DAC_OUTPUT_RIGHT); bdev->cache_fm_audio_ctrl0 |= channels; err = bcm2048_send_command(bdev, BCM2048_I2C_FM_AUDIO_CTRL0, bdev->cache_fm_audio_ctrl0); mutex_unlock(&bdev->mutex); return err; }

Contributors

PersonTokensPropCommitsCommitProp
Hans Verkuil66100.00%1100.00%
Total66100.00%1100.00%


static int bcm2048_get_dac_output(struct bcm2048_device *bdev) { int err; u8 value; mutex_lock(&bdev->mutex); err = bcm2048_recv_command(bdev, BCM2048_I2C_FM_AUDIO_CTRL0, &value); mutex_unlock(&bdev->mutex); if (!err) return value & (BCM2048_DAC_OUTPUT_LEFT | BCM2048_DAC_OUTPUT_RIGHT); return err; }

Contributors

PersonTokensPropCommitsCommitProp
Hans Verkuil62100.00%1100.00%
Total62100.00%1100.00%


static int bcm2048_set_fm_search_rssi_threshold(struct bcm2048_device *bdev, u8 threshold) { int err; mutex_lock(&bdev->mutex); threshold &= BCM2048_SEARCH_RSSI_THRESHOLD; bdev->cache_fm_search_ctrl0 &= ~BCM2048_SEARCH_RSSI_THRESHOLD; bdev->cache_fm_search_ctrl0 |= threshold; err = bcm2048_send_command(bdev, BCM2048_I2C_FM_SEARCH_CTRL0, bdev->cache_fm_search_ctrl0); mutex_unlock(&bdev->mutex); return err; }

Contributors

PersonTokensPropCommitsCommitProp
Hans Verkuil66100.00%1100.00%
Total66100.00%1100.00%


static int bcm2048_get_fm_search_rssi_threshold(struct bcm2048_device *bdev) { int err; u8 value; mutex_lock(&bdev->mutex); err = bcm2048_recv_command(bdev, BCM2048_I2C_FM_SEARCH_CTRL0, &value); mutex_unlock(&bdev->mutex); if (!err) return value & BCM2048_SEARCH_RSSI_THRESHOLD; return err; }

Contributors

PersonTokensPropCommitsCommitProp
Hans Verkuil58100.00%1100.00%
Total58100.00%1100.00%


static int bcm2048_set_fm_search_mode_direction(struct bcm2048_device *bdev, u8 direction) { int err; mutex_lock(&bdev->mutex); bdev->cache_fm_search_ctrl0 &= ~BCM2048_SEARCH_DIRECTION; if (direction) bdev->cache_fm_search_ctrl0 |= BCM2048_SEARCH_DIRECTION; err = bcm2048_send_command(bdev, BCM2048_I2C_FM_SEARCH_CTRL0, bdev->cache_fm_search_ctrl0); mutex_unlock(&bdev->mutex); return err; }

Contributors

PersonTokensPropCommitsCommitProp
Hans Verkuil66100.00%1100.00%
Total66100.00%1100.00%


static int bcm2048_get_fm_search_mode_direction(struct bcm2048_device *bdev) { int err; u8 value; mutex_lock(&bdev->mutex); err = bcm2048_recv_command(bdev, BCM2048_I2C_FM_SEARCH_CTRL0, &value); mutex_unlock(&bdev->mutex); if (!err && (value & BCM2048_SEARCH_DIRECTION)) return BCM2048_SEARCH_DIRECTION_UP; return err; }

Contributors

PersonTokensPropCommitsCommitProp
Hans Verkuil62100.00%1100.00%
Total62100.00%1100.00%


static int bcm2048_set_fm_search_tune_mode(struct bcm2048_device *bdev, u8 mode) { int err, timeout, restart_rds = 0; u8 value, flags; value = mode & BCM2048_FM_AUTO_SEARCH; flags = BCM2048_FM_FLAG_SEARCH_TUNE_FINISHED | BCM2048_FM_FLAG_SEARCH_TUNE_FAIL; mutex_lock(&bdev->mutex); /* * If RDS is enabled, and frequency is changed, RDS quits working. * Thus, always restart RDS if it's enabled. Moreover, RDS must * not be enabled while changing the frequency because it can * provide a race to the mutex from the workqueue handler if RDS * IRQ occurs while waiting for frequency changed IRQ. */ if (bcm2048_get_rds_no_lock(bdev)) { err = bcm2048_set_rds_no_lock(bdev, 0); if (err) goto unlock; restart_rds = 1; } err = bcm2048_send_command(bdev, BCM2048_I2C_FM_RDS_MASK0, flags); if (err) goto unlock; bcm2048_send_command(bdev, BCM2048_I2C_FM_SEARCH_TUNE_MODE, value); if (mode != BCM2048_FM_AUTO_SEARCH_MODE) timeout = BCM2048_DEFAULT_TIMEOUT; else timeout = BCM2048_AUTO_SEARCH_TIMEOUT; if (!wait_for_completion_timeout(&bdev->compl, msecs_to_jiffies(timeout))) dev_err(&bdev->client->dev, "IRQ timeout.\n"); if (value) if (!bdev->scan_state) err = -EIO; unlock: if (restart_rds) err |= bcm2048_set_rds_no_lock(bdev, 1); mutex_unlock(&bdev->mutex); return err; }

Contributors

PersonTokensPropCommitsCommitProp
Hans Verkuil190100.00%1100.00%
Total190100.00%1100.00%


static int bcm2048_get_fm_search_tune_mode(struct bcm2048_device *bdev) { int err; u8 value; mutex_lock(&bdev->mutex); err = bcm2048_recv_command(bdev, BCM2048_I2C_FM_SEARCH_TUNE_MODE, &value); mutex_unlock(&bdev->mutex); if (!err) return value & BCM2048_FM_AUTO_SEARCH; return err; }

Contributors

PersonTokensPropCommitsCommitProp
Hans Verkuil58100.00%1100.00%
Total58100.00%1100.00%


static int bcm2048_set_rds_b_block_mask(struct bcm2048_device *bdev, u16 mask) { int err; mutex_lock(&bdev->mutex); err = bcm2048_send_command(bdev, BCM2048_I2C_RDS_BLKB_MASK0, lsb(mask)); err |= bcm2048_send_command(bdev, BCM2048_I2C_RDS_BLKB_MASK1, msb(mask)); mutex_unlock(&bdev->mutex); return err; }

Contributors

PersonTokensPropCommitsCommitProp
Hans Verkuil64100.00%1100.00%
Total64100.00%1100.00%


static int bcm2048_get_rds_b_block_mask(struct bcm2048_device *bdev) { int err; u8 lsb = 0, msb = 0; mutex_lock(&bdev->mutex); err = bcm2048_recv_command(bdev, BCM2048_I2C_RDS_BLKB_MASK0, &lsb); err |= bcm2048_recv_command(bdev, BCM2048_I2C_RDS_BLKB_MASK1, &msb); mutex_unlock(&bdev->mutex); if (!err) return compose_u16(msb, lsb); return err; }

Contributors

PersonTokensPropCommitsCommitProp
Hans Verkuil7594.94%150.00%
Andrew Milkovich45.06%150.00%
Total79100.00%2100.00%


static int bcm2048_set_rds_b_block_match(struct bcm2048_device *bdev, u16 match) { int err; mutex_lock(&bdev->mutex); err = bcm2048_send_command(bdev, BCM2048_I2C_RDS_BLKB_MATCH0, lsb(match)); err |= bcm2048_send_command(bdev, BCM2048_I2C_RDS_BLKB_MATCH1, msb(match)); mutex_unlock(&bdev->mutex); return err; }

Contributors

PersonTokensPropCommitsCommitProp
Hans Verkuil64100.00%1100.00%
Total64100.00%1100.00%


static int bcm2048_get_rds_b_block_match(struct bcm2048_device *bdev) { int err; u8 lsb = 0, msb = 0; mutex_lock(&bdev->mutex); err = bcm2048_recv_command(bdev, BCM2048_I2C_RDS_BLKB_MATCH0, &lsb); err |= bcm2048_recv_command(bdev, BCM2048_I2C_RDS_BLKB_MATCH1, &msb); mutex_unlock(&bdev->mutex); if (!err) return compose_u16(msb, lsb); return err; }

Contributors

PersonTokensPropCommitsCommitProp
Hans Verkuil7594.94%150.00%
Andrew Milkovich45.06%150.00%
Total79100.00%2100.00%


static int bcm2048_set_rds_pi_mask(struct bcm2048_device *bdev, u16 mask) { int err; mutex_lock(&bdev->mutex); err = bcm2048_send_command(bdev, BCM2048_I2C_RDS_PI_MASK0, lsb(mask)); err |= bcm2048_send_command(bdev, BCM2048_I2C_RDS_PI_MASK1, msb(mask)); mutex_unlock(&bdev->mutex); return err; }

Contributors

PersonTokensPropCommitsCommitProp
Hans Verkuil64100.00%1100.00%
Total64100.00%1100.00%


static int bcm2048_get_rds_pi_mask(struct bcm2048_device *bdev) { int err; u8 lsb = 0, msb = 0; mutex_lock(&bdev->mutex); err = bcm2048_recv_command(bdev, BCM2048_I2C_RDS_PI_MASK0, &lsb); err |= bcm2048_recv_command(bdev, BCM2048_I2C_RDS_PI_MASK1, &msb); mutex_unlock(&bdev->mutex); if (!err) return compose_u16(msb, lsb); return err; }

Contributors

PersonTokensPropCommitsCommitProp
Hans Verkuil7594.94%150.00%
Andrew Milkovich45.06%150.00%
Total79100.00%2100.00%


static int bcm2048_set_rds_pi_match(struct bcm2048_device *bdev, u16 match) { int err; mutex_lock(&bdev->mutex); err = bcm2048_send_command(bdev, BCM2048_I2C_RDS_PI_MATCH0, lsb(match)); err |= bcm2048_send_command(bdev, BCM2048_I2C_RDS_PI_MATCH1, msb(match)); mutex_unlock(&bdev->mutex); return err; }

Contributors

PersonTokensPropCommitsCommitProp
Hans Verkuil64100.00%1100.00%
Total64100.00%1100.00%


static int bcm2048_get_rds_pi_match(struct bcm2048_device *bdev) { int err; u8 lsb = 0, msb = 0; mutex_lock(&bdev->mutex); err = bcm2048_recv_command(bdev, BCM2048_I2C_RDS_PI_MATCH0, &lsb); err |= bcm2048_recv_command(bdev, BCM2048_I2C_RDS_PI_MATCH1, &msb); mutex_unlock(&bdev->mutex); if (!err) return compose_u16(msb, lsb); return err; }

Contributors

PersonTokensPropCommitsCommitProp
Hans Verkuil7594.94%150.00%
Andrew Milkovich45.06%150.00%
Total79100.00%2100.00%


static int bcm2048_set_fm_rds_mask(struct bcm2048_device *bdev, u16 mask) { int err; mutex_lock(&bdev->mutex); err = bcm2048_send_command(bdev, BCM2048_I2C_FM_RDS_MASK0, lsb(mask)); err |= bcm2048_send_command(bdev, BCM2048_I2C_FM_RDS_MASK1, msb(mask)); mutex_unlock(&bdev->mutex); return err; }

Contributors

PersonTokensPropCommitsCommitProp
Hans Verkuil64100.00%1100.00%
Total64100.00%1100.00%


static int bcm2048_get_fm_rds_mask(struct bcm2048_device *bdev) { int err; u8 value0 = 0, value1 = 0; mutex_lock(&bdev->mutex); err = bcm2048_recv_command(bdev, BCM2048_I2C_FM_RDS_MASK0, &value0); err |= bcm2048_recv_command(bdev, BCM2048_I2C_FM_RDS_MASK1, &value1); mutex_unlock(&bdev->mutex); if (!err) return compose_u16(value1, value0); return err; }

Contributors

PersonTokensPropCommitsCommitProp
Hans Verkuil7594.94%150.00%
Andrew Milkovich45.06%150.00%
Total79100.00%2100.00%


static int bcm2048_get_fm_rds_flags(struct bcm2048_device *bdev) { int err; u8 value0 = 0, value1 = 0; mutex_lock(&bdev->mutex); err = bcm2048_recv_command(bdev, BCM2048_I2C_FM_RDS_FLAG0, &value0); err |= bcm2048_recv_command(bdev, BCM2048_I2C_FM_RDS_FLAG1, &value1); mutex_unlock(&bdev->mutex); if (!err) return compose_u16(value1, value0); return err; }

Contributors

PersonTokensPropCommitsCommitProp
Hans Verkuil7594.94%150.00%
Andrew Milkovich45.06%150.00%
Total79100.00%2100.00%


static int bcm2048_get_region_bottom_frequency(struct bcm2048_device *bdev) { return bdev->region_info.bottom_frequency; }

Contributors

PersonTokensPropCommitsCommitProp
Hans Verkuil18100.00%1100.00%
Total18100.00%1100.00%


static int bcm2048_get_region_top_frequency(struct bcm2048_device *bdev) { return bdev->region_info.top_frequency; }

Contributors

PersonTokensPropCommitsCommitProp
Hans Verkuil18100.00%1100.00%
Total18100.00%1100.00%


static int bcm2048_set_fm_best_tune_mode(struct bcm2048_device *bdev, u8 mode) { int err; u8 value = 0; mutex_lock(&bdev->mutex); /* Perform read as the manual indicates */ err = bcm2048_recv_command(bdev, BCM2048_I2C_FM_BEST_TUNE_MODE, &value); value &= ~BCM2048_BEST_TUNE_MODE; if (mode) value |= BCM2048_BEST_TUNE_MODE; err |= bcm2048_send_command(bdev, BCM2048_I2C_FM_BEST_TUNE_MODE, value); mutex_unlock(&bdev->mutex); return err; }

Contributors

PersonTokensPropCommitsCommitProp
Hans Verkuil7697.44%150.00%
Andrew Milkovich22.56%150.00%
Total78100.00%2100.00%


static int bcm2048_get_fm_best_tune_mode(struct bcm2048_device *bdev) { int err; u8 value; mutex_lock(&bdev->mutex); err = bcm2048_recv_command(bdev, BCM2048_I2C_FM_BEST_TUNE_MODE, &value); mutex_unlock(&bdev->mutex); if (!err && (value & BCM2048_BEST_TUNE_MODE)) return BCM2048_ITEM_ENABLED; return err; }

Contributors

PersonTokensPropCommitsCommitProp
Hans Verkuil62100.00%1100.00%
Total62100.00%1100.00%


static int bcm2048_get_fm_carrier_error(struct bcm2048_device *bdev) { int err = 0; s8 value; mutex_lock(&bdev->mutex); err = bcm2048_recv_command(bdev, BCM2048_I2C_FM_CARRIER, &value); mutex_unlock(&bdev->mutex); if (!err) return value; return err; }

Contributors

PersonTokensPropCommitsCommitProp
Hans Verkuil58100.00%1100.00%
Total58100.00%1100.00%


static int bcm2048_get_fm_rssi(struct bcm2048_device *bdev) { int err; s8 value; mutex_lock(&bdev->mutex); err = bcm2048_recv_command(bdev, BCM2048_I2C_FM_RSSI, &value); mutex_unlock(&bdev->mutex); if (!err) return value; return err; }

Contributors

PersonTokensPropCommitsCommitProp
Hans Verkuil56100.00%1100.00%
Total56100.00%1100.00%


static int bcm2048_set_rds_wline(struct bcm2048_device *bdev, u8 wline) { int err; mutex_lock(&bdev->mutex); err = bcm2048_send_command(bdev, BCM2048_I2C_RDS_WLINE, wline); if (!err) bdev->fifo_size = wline; mutex_unlock(&bdev->mutex); return err; }

Contributors

PersonTokensPropCommitsCommitProp
Hans Verkuil58100.00%1100.00%
Total58100.00%1100.00%


static int bcm2048_get_rds_wline(struct bcm2048_device *bdev) { int err; u8 value; mutex_lock(&bdev->mutex); err = bcm2048_recv_command(bdev, BCM2048_I2C_RDS_WLINE, &value); mutex_unlock(&bdev->mutex); if (!err) { bdev->fifo_size = value; return value; } return err; }

Contributors

PersonTokensPropCommitsCommitProp
Hans Verkuil64100.00%1100.00%
Total64100.00%1100.00%


static int bcm2048_checkrev(struct bcm2048_device *bdev) { int err; u8 version; mutex_lock(&bdev->mutex); err = bcm2048_recv_command(bdev, BCM2048_I2C_FM_RDS_REV, &version); mutex_unlock(&bdev->mutex); if (!err) { dev_info(&bdev->client->dev, "BCM2048 Version 0x%x\n", version); return version; } return err; }

Contributors

PersonTokensPropCommitsCommitProp
Hans Verkuil72100.00%1100.00%
Total72100.00%1100.00%


static int bcm2048_get_rds_rt(struct bcm2048_device *bdev, char *data) { int err = 0, i, j = 0, ce = 0, cr = 0; char data_buffer[BCM2048_MAX_RDS_RT + 1]; mutex_lock(&bdev->mutex); if (!bdev->rds_info.text_len) { err = -EINVAL; goto unlock; } for (i = 0; i < BCM2048_MAX_RDS_RT; i++) { if (bdev->rds_info.rds_rt[i]) { ce = i; /* Skip the carriage return */ if (bdev->rds_info.rds_rt[i] != 0x0d) { data_buffer[j++] = bdev->rds_info.rds_rt[i]; } else { cr = i; break; } } } if (j <= BCM2048_MAX_RDS_RT) data_buffer[j] = 0; for (i = 0; i < BCM2048_MAX_RDS_RT; i++) { if (!bdev->rds_info.rds_rt[i]) { if (cr && (i < cr)) { err = -EBUSY; goto unlock; } if (i < ce) { if (cr && (i >= cr)) break; err = -EBUSY; goto unlock; } } } memcpy(data, data_buffer, sizeof(data_buffer)); unlock: mutex_unlock(&bdev->mutex); return err; }

Contributors

PersonTokensPropCommitsCommitProp
Hans Verkuil254100.00%1100.00%
Total254100.00%1100.00%


static int bcm2048_get_rds_ps(struct bcm2048_device *bdev, char *data) { int err = 0, i, j = 0; char data_buffer[BCM2048_MAX_RDS_PS + 1]; mutex_lock(&bdev->mutex); if (!bdev->rds_info.text_len) { err = -EINVAL; goto unlock; } for (i = 0; i < BCM2048_MAX_RDS_PS; i++) { if (bdev->rds_info.rds_ps[i]) { data_buffer[j++] = bdev->rds_info.rds_ps[i]; } else { if (i < (BCM2048_MAX_RDS_PS - 1)) { err = -EBUSY; goto unlock; } } } if (j <= BCM2048_MAX_RDS_PS) data_buffer[j] = 0; memcpy(data, data_buffer, sizeof(data_buffer)); unlock: mutex_unlock(&bdev->mutex); return err; }

Contributors

PersonTokensPropCommitsCommitProp
Hans Verkuil165100.00%1100.00%
Total165100.00%1100.00%


static void bcm2048_parse_rds_pi(struct bcm2048_device *bdev) { int i, cnt = 0; u16 pi; for (i = 0; i < bdev->fifo_size; i += BCM2048_RDS_FIFO_DUPLE_SIZE) { /* Block A match, only data without crc errors taken */ if (bdev->rds_info.radio_text[i] == BCM2048_RDS_BLOCK_A) { pi = (bdev->rds_info.radio_text[i + 1] << 8) + bdev->rds_info.radio_text[i + 2]; if (!bdev->rds_info.rds_pi) { bdev->rds_info.rds_pi = pi; return; } if (pi != bdev->rds_info.rds_pi) { cnt++; if (cnt > 3) { bdev->rds_info.rds_pi = pi; cnt = 0; } } else { cnt = 0; } } } }

Contributors

PersonTokensPropCommitsCommitProp
Hans Verkuil145100.00%1100.00%
Total145100.00%1100.00%


static int bcm2048_rds_block_crc(struct bcm2048_device *bdev, int i) { return bdev->rds_info.radio_text[i] & BCM2048_RDS_CRC_MASK; }

Contributors

PersonTokensPropCommitsCommitProp
Hans Verkuil26100.00%1100.00%
Total26100.00%1100.00%


static void bcm2048_parse_rds_rt_block(struct bcm2048_device *bdev, int i, int index, int crc) { /* Good data will overwrite poor data */ if (crc) { if (!bdev->rds_info.rds_rt[index]) bdev->rds_info.rds_rt[index] = bdev->rds_info.radio_text[i + 1]; if (!bdev->rds_info.rds_rt[index + 1]) bdev->rds_info.rds_rt[index + 1] = bdev->rds_info.radio_text[i + 2]; } else { bdev->rds_info.rds_rt[index] = bdev->rds_info.radio_text[i + 1]; bdev->rds_info.rds_rt[index + 1] = bdev->rds_info.radio_text[i + 2]; } }

Contributors

PersonTokensPropCommitsCommitProp
Hans Verkuil140100.00%1100.00%
Total140100.00%1100.00%


static int bcm2048_parse_rt_match_b(struct bcm2048_device *bdev, int i) { int crc, rt_id, rt_group_b, rt_ab, index = 0; crc = bcm2048_rds_block_crc(bdev, i); if (crc == BCM2048_RDS_CRC_UNRECOVARABLE) return -EIO; if ((bdev->rds_info.radio_text[i] & BCM2048_RDS_BLOCK_MASK) == BCM2048_RDS_BLOCK_B) { rt_id = bdev->rds_info.radio_text[i + 1] & BCM2048_RDS_BLOCK_MASK; rt_group_b = bdev->rds_info.radio_text[i + 1] & BCM2048_RDS_GROUP_AB_MASK; rt_ab = bdev->rds_info.radio_text[i + 2] & BCM2048_RDS_RT_AB_MASK; if (rt_group_b != bdev->rds_info.rds_rt_group_b) { memset(bdev->rds_info.rds_rt, 0, sizeof(bdev->rds_info.rds_rt)); bdev->rds_info.rds_rt_group_b = rt_group_b; } if (rt_id == BCM2048_RDS_RT) { /* A to B or (vice versa), means: clear screen */ if (rt_ab != bdev->rds_info.rds_rt_ab) { memset(bdev->rds_info.rds_rt, 0, sizeof(bdev->rds_info.rds_rt)); bdev->rds_info.rds_rt_ab = rt_ab; } index = bdev->rds_info.radio_text[i + 2] & BCM2048_RDS_RT_INDEX; if (bdev->rds_info.rds_rt_group_b) index <<= 1; else index <<= 2; return index; } } return -EIO; }

Contributors

PersonTokensPropCommitsCommitProp
Hans Verkuil238100.00%1100.00%
Total238100.00%1100.00%


static int bcm2048_parse_rt_match_c(struct bcm2048_device *bdev, int i, int index) { int crc; crc = bcm2048_rds_block_crc(bdev, i); if (crc == BCM2048_RDS_CRC_UNRECOVARABLE) return 0; BUG_ON((index+2) >= BCM2048_MAX_RDS_RT); if ((bdev->rds_info.radio_text[i] & BCM2048_RDS_BLOCK_MASK) == BCM2048_RDS_BLOCK_C) { if (bdev->rds_info.rds_rt_group_b) return 1; bcm2048_parse_rds_rt_block(bdev, i, index, crc); return 1; } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Hans Verkuil96100.00%1100.00%
Total96100.00%1100.00%


static void bcm2048_parse_rt_match_d(struct bcm2048_device *bdev, int i, int index) { int crc; crc = bcm2048_rds_block_crc(bdev, i); if (crc == BCM2048_RDS_CRC_UNRECOVARABLE) return; BUG_ON((index+4) >= BCM2048_MAX_RDS_RT); if ((bdev->rds_info.radio_text[i] & BCM2048_RDS_BLOCK_MASK) == BCM2048_RDS_BLOCK_D) bcm2048_parse_rds_rt_block(bdev, i, index + 2, crc); }

Contributors

PersonTokensPropCommitsCommitProp
Hans Verkuil77100.00%1100.00%
Total77100.00%1100.00%


static void bcm2048_parse_rds_rt(struct bcm2048_device *bdev) { int i, index = 0, crc, match_b = 0, match_c = 0, match_d = 0; for (i = 0; i < bdev->fifo_size; i += BCM2048_RDS_FIFO_DUPLE_SIZE) { if (match_b) { match_b = 0; index = bcm2048_parse_rt_match_b(bdev, i); if (index >= 0 && index <= (BCM2048_MAX_RDS_RT - 5)) match_c = 1; continue; } else if (match_c) { match_c = 0; if (bcm2048_parse_rt_match_c(bdev, i, index)) match_d = 1; continue; } else if (match_d) { match_d = 0; bcm2048_parse_rt_match_d(bdev, i, index); continue; } /* Skip erroneous blocks due to messed up A block altogether */ if ((bdev->rds_info.radio_text[i] & BCM2048_RDS_BLOCK_MASK) == BCM2048_RDS_BLOCK_A) { crc = bcm2048_rds_block_crc(bdev, i); if (crc == BCM2048_RDS_CRC_UNRECOVARABLE) continue; /* Synchronize to a good RDS PI */ if (((bdev->rds_info.radio_text[i + 1] << 8) + bdev->rds_info.radio_text[i + 2]) == bdev->rds_info.rds_pi) match_b = 1; } } }

Contributors

PersonTokensPropCommitsCommitProp
Hans Verkuil21199.06%133.33%
Luis de Bethencourt10.47%133.33%
Amarjargal Gundjalam10.47%133.33%
Total213100.00%3100.00%


static void bcm2048_parse_rds_ps_block(struct bcm2048_device *bdev, int i, int index, int crc) { /* Good data will overwrite poor data */ if (crc) { if (!bdev->rds_info.rds_ps[index]) bdev->rds_info.rds_ps[index] = bdev->rds_info.radio_text[i + 1]; if (!bdev->rds_info.rds_ps[index + 1]) bdev->rds_info.rds_ps[index + 1] = bdev->rds_info.radio_text[i + 2]; } else { bdev->rds_info.rds_ps[index] = bdev->rds_info.radio_text[i + 1]; bdev->rds_info.rds_ps[index + 1] = bdev->rds_info.radio_text[i + 2]; } }

Contributors

PersonTokensPropCommitsCommitProp
Hans Verkuil140100.00%1100.00%
Total140100.00%1100.00%


static int bcm2048_parse_ps_match_c(struct bcm2048_device *bdev, int i, int index) { int crc; crc = bcm2048_rds_block_crc(bdev, i); if (crc == BCM2048_RDS_CRC_UNRECOVARABLE) return 0; if ((bdev->rds_info.radio_text[i] & BCM2048_RDS_BLOCK_MASK) == BCM2048_RDS_BLOCK_C) return 1; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Hans Verkuil61100.00%1100.00%
Total61100.00%1100.00%


static void bcm2048_parse_ps_match_d(struct bcm2048_device *bdev, int i, int index) { int crc; crc = bcm2048_rds_block_crc(bdev, i); if (crc == BCM2048_RDS_CRC_UNRECOVARABLE) return; if ((bdev->rds_info.radio_text[i] & BCM2048_RDS_BLOCK_MASK) == BCM2048_RDS_BLOCK_D) bcm2048_parse_rds_ps_block(bdev, i, index, crc); }

Contributors

PersonTokensPropCommitsCommitProp
Hans Verkuil64100.00%1100.00%
Total64100.00%1100.00%


static int bcm2048_parse_ps_match_b(struct bcm2048_device *bdev, int i) { int crc, index, ps_id, ps_group; crc = bcm2048_rds_block_crc(bdev, i); if (crc == BCM2048_RDS_CRC_UNRECOVARABLE) return -EIO; /* Block B Radio PS match */ if ((bdev->rds_info.radio_text[i] & BCM2048_RDS_BLOCK_MASK) == BCM2048_RDS_BLOCK_B) { ps_id = bdev->rds_info.radio_text[i + 1] & BCM2048_RDS_BLOCK_MASK; ps_group = bdev->rds_info.radio_text[i + 1] & BCM2048_RDS_GROUP_AB_MASK; /* * Poor RSSI will lead to RDS data corruption * So using 3 (same) sequential values to justify major changes */ if (ps_group != bdev->rds_info.rds_ps_group) { if (crc == BCM2048_RDS_CRC_NONE) { bdev->rds_info.rds_ps_group_cnt++; if (bdev->rds_info.rds_ps_group_cnt > 2) { bdev->rds_info.rds_ps_group = ps_group; bdev->rds_info.rds_ps_group_cnt = 0; dev_err(&bdev->client->dev, "RDS PS Group change!\n"); } else { return -EIO; } } else { bdev->rds_info.rds_ps_group_cnt = 0; } } if (ps_id == BCM2048_RDS_PS) { index = bdev->rds_info.radio_text[i + 2] & BCM2048_RDS_PS_INDEX; index <<= 1; return index; } } return -EIO; }

Contributors

PersonTokensPropCommitsCommitProp
Hans Verkuil212100.00%1100.00%
Total212100.00%1100.00%


static void bcm2048_parse_rds_ps(struct bcm2048_device *bdev) { int i, index = 0, crc, match_b = 0, match_c = 0, match_d = 0; for (i = 0; i < bdev->fifo_size; i += BCM2048_RDS_FIFO_DUPLE_SIZE) { if (match_b) { match_b = 0; index = bcm2048_parse_ps_match_b(bdev, i); if (index >= 0 && index < (BCM2048_MAX_RDS_PS - 1)) match_c = 1; continue; } else if (match_c) { match_c = 0; if (bcm2048_parse_ps_match_c(bdev, i, index)) match_d = 1; continue; } else if (match_d) { match_d = 0; bcm2048_parse_ps_match_d(bdev, i, index); continue; } /* Skip erroneous blocks due to messed up A block altogether */ if ((bdev->rds_info.radio_text[i] & BCM2048_RDS_BLOCK_MASK) == BCM2048_RDS_BLOCK_A) { crc = bcm2048_rds_block_crc(bdev, i); if (crc == BCM2048_RDS_CRC_UNRECOVARABLE) continue; /* Synchronize to a good RDS PI */ if (((bdev->rds_info.radio_text[i + 1] << 8) + bdev->rds_info.radio_text[i + 2]) == bdev->rds_info.rds_pi) match_b = 1; } } }

Contributors

PersonTokensPropCommitsCommitProp
Hans Verkuil21299.53%150.00%
Amarjargal Gundjalam10.47%150.00%
Total213100.00%2100.00%


static void bcm2048_rds_fifo_receive(struct bcm2048_device *bdev) { int err; mutex_lock(&bdev->mutex); err = bcm2048_recv_duples(bdev, BCM2048_I2C_RDS_DATA, bdev->rds_info.radio_text, bdev->fifo_size); if (err != 2) { dev_err(&bdev->client->dev, "RDS Read problem\n"); mutex_unlock(&bdev->mutex); return; } bdev->rds_info.text_len = bdev->fifo_size; bcm2048_parse_rds_pi(bdev); bcm2048_parse_rds_rt(bdev); bcm2048_parse_rds_ps(bdev); mutex_unlock(&bdev->mutex); wake_up_interruptible(&bdev->read_queue); }

Contributors

PersonTokensPropCommitsCommitProp
Hans Verkuil10392.79%150.00%
Wei Yongjun87.21%150.00%
Total111100.00%2100.00%


static int bcm2048_get_rds_data(struct bcm2048_device *bdev, char *data) { int err = 0, i, p = 0; char *data_buffer; mutex_lock(&bdev->mutex); if (!bdev->rds_info.text_len) { err = -EINVAL; goto unlock; } data_buffer = kcalloc(BCM2048_MAX_RDS_RADIO_TEXT, 5, GFP_KERNEL); if (!data_buffer) { err = -ENOMEM; goto unlock; } for (i = 0; i < bdev->rds_info.text_len; i++) { p += sprintf(data_buffer + p, "%x ", bdev->rds_info.radio_text[i]); } memcpy(data, data_buffer, p); kfree(data_buffer); unlock: mutex_unlock(&bdev->mutex); return err; }

Contributors

PersonTokensPropCommitsCommitProp
Hans Verkuil14798.66%150.00%
Navya Sri Nizamkari21.34%150.00%
Total149100.00%2100.00%

/* * BCM2048 default initialization sequence */
static int bcm2048_init(struct bcm2048_device *bdev) { int err; err = bcm2048_set_power_state(bdev, BCM2048_POWER_ON); if (err < 0) goto exit; err = bcm2048_set_audio_route(bdev, BCM2048_AUDIO_ROUTE_DAC); if (err < 0) goto exit; err = bcm2048_set_dac_output(bdev, BCM2048_DAC_OUTPUT_LEFT | BCM2048_DAC_OUTPUT_RIGHT); exit: return err; }

Contributors

PersonTokensPropCommitsCommitProp
Hans Verkuil66100.00%1100.00%
Total66100.00%1100.00%

/* * BCM2048 default deinitialization sequence */
static int bcm2048_deinit(struct bcm2048_device *bdev) { int err; err = bcm2048_set_audio_route(bdev, 0); if (err < 0) return err; err = bcm2048_set_dac_output(bdev, 0); if (err < 0) return err; return bcm2048_set_power_state(bdev, BCM2048_POWER_OFF); }

Contributors

PersonTokensPropCommitsCommitProp
Hans Verkuil5187.93%133.33%
Sandhya Bankar610.34%133.33%
Rehas Sachdeva11.72%133.33%
Total58100.00%3100.00%

/* * BCM2048 probe sequence */
static int bcm2048_probe(struct bcm2048_device *bdev) { int err; err = bcm2048_set_power_state(bdev, BCM2048_POWER_ON); if (err < 0) goto unlock; err = bcm2048_checkrev(bdev); if (err < 0) goto unlock; err = bcm2048_set_mute(bdev, BCM2048_DEFAULT_MUTE); if (err < 0) goto unlock; err = bcm2048_set_region(bdev, BCM2048_DEFAULT_REGION); if (err < 0) goto unlock; err = bcm2048_set_fm_search_rssi_threshold(bdev, BCM2048_DEFAULT_RSSI_THRESHOLD); if (err < 0) goto unlock; err = bcm2048_set_fm_automatic_stereo_mono(bdev, BCM2048_ITEM_ENABLED); if (err < 0) goto unlock; err = bcm2048_get_rds_wline(bdev); if (err < BCM2048_DEFAULT_RDS_WLINE) err = bcm2048_set_rds_wline(bdev, BCM2048_DEFAULT_RDS_WLINE); if (err < 0) goto unlock; err = bcm2048_set_power_state(bdev, BCM2048_POWER_OFF); init_waitqueue_head(&bdev->read_queue); bdev->rds_data_available = 0; bdev->rd_index = 0; bdev->users = 0; unlock: return err; }

Contributors

PersonTokensPropCommitsCommitProp
Hans Verkuil191100.00%1100.00%
Total191100.00%1100.00%

/* * BCM2048 workqueue handler */
static void bcm2048_work(struct work_struct *work) { struct bcm2048_device *bdev; u8 flag_lsb = 0, flag_msb = 0, flags; bdev = container_of(work, struct bcm2048_device, work); bcm2048_recv_command(bdev, BCM2048_I2C_FM_RDS_FLAG0, &flag_lsb); bcm2048_recv_command(bdev, BCM2048_I2C_FM_RDS_FLAG1, &flag_msb); if (flag_lsb & (BCM2048_FM_FLAG_SEARCH_TUNE_FINISHED | BCM2048_FM_FLAG_SEARCH_TUNE_FAIL)) { if (flag_lsb & BCM2048_FM_FLAG_SEARCH_TUNE_FAIL) bdev->scan_state = BCM2048_SCAN_FAIL; else bdev->scan_state = BCM2048_SCAN_OK; complete(&bdev->compl); } if (flag_msb & BCM2048_RDS_FLAG_FIFO_WLINE) { bcm2048_rds_fifo_receive(bdev); if (bdev->rds_state) { flags = BCM2048_RDS_FLAG_FIFO_WLINE; bcm2048_send_command(bdev, BCM2048_I2C_FM_RDS_MASK1, flags); } bdev->rds_data_available = 1; bdev->rd_index = 0; /* new data, new start */ } }

Contributors

PersonTokensPropCommitsCommitProp
Hans Verkuil14197.24%150.00%
Andrew Milkovich42.76%150.00%
Total145100.00%2100.00%

/* * BCM2048 interrupt handler */
static irqreturn_t bcm2048_handler(int irq, void *dev) { struct bcm2048_device *bdev = dev; dev_dbg(&bdev->client->dev, "IRQ called, queuing work\n"); if (bdev->power_state) schedule_work(&bdev->work); return IRQ_HANDLED; }

Contributors

PersonTokensPropCommitsCommitProp
Hans Verkuil49100.00%1100.00%
Total49100.00%1100.00%

/* * BCM2048 sysfs interface definitions */ #define property_write(prop, type, mask, check) \ static ssize_t bcm2048_##prop##_write(struct device *dev, \ struct device_attribute *attr, \ const char *buf, \ size_t count) \ { \ struct bcm2048_device *bdev = dev_get_drvdata(dev); \ type value; \ int err; \ \ if (!bdev) \ return -ENODEV; \ \ if (sscanf(buf, mask, &value) != 1) \ return -EINVAL; \ \ if (check) \ return -EDOM; \ \ err = bcm2048_set_##prop(bdev, value); \ \ return err < 0 ? err : count; \ } #define property_read(prop, size, mask) \ static ssize_t bcm2048_##prop##_read(struct device *dev, \ struct device_attribute *attr, \ char *buf) \ { \ struct bcm2048_device *bdev = dev_get_drvdata(dev); \ int value; \ \ if (!bdev) \ return -ENODEV; \ \ value = bcm2048_get_##prop(bdev); \ \ if (value >= 0) \ value = sprintf(buf, mask "\n", value); \ \ return value; \ } #define property_signed_read(prop, size, mask) \ static ssize_t bcm2048_##prop##_read(struct device *dev, \ struct device_attribute *attr, \ char *buf) \ { \ struct bcm2048_device *bdev = dev_get_drvdata(dev); \ size value; \ \ if (!bdev) \ return -ENODEV; \ \ value = bcm2048_get_##prop(bdev); \ \ return sprintf(buf, mask "\n", value); \ } #define DEFINE_SYSFS_PROPERTY(prop, signal, size, mask, check) \ property_write(prop, signal size, mask, check) \ property_read(prop, size, mask) #define property_str_read(prop, size) \ static ssize_t bcm2048_##prop##_read(struct device *dev, \ struct device_attribute *attr, \ char *buf) \ { \ struct bcm2048_device *bdev = dev_get_drvdata(dev); \ int count; \ u8 *out; \ \ if (!bdev) \ return -ENODEV; \ \ out = kzalloc(size + 1, GFP_KERNEL); \ if (!out) \ return -ENOMEM; \ \ bcm2048_get_##prop(bdev, out); \ count = sprintf(buf, "%s\n", out); \ \ kfree(out); \ \ return count; \ } DEFINE_SYSFS_PROPERTY(power_state, unsigned, int, "%u", 0) DEFINE_SYSFS_PROPERTY(mute, unsigned, int, "%u", 0) DEFINE_SYSFS_PROPERTY(audio_route, unsigned, int, "%u", 0) DEFINE_SYSFS_PROPERTY(dac_output, unsigned, int, "%u", 0) DEFINE_SYSFS_PROPERTY(fm_hi_lo_injection, unsigned, int, "%u", 0) DEFINE_SYSFS_PROPERTY(fm_frequency, unsigned, int, "%u", 0) DEFINE_SYSFS_PROPERTY(fm_af_frequency, unsigned, int, "%u", 0) DEFINE_SYSFS_PROPERTY(fm_deemphasis, unsigned, int, "%u", 0) DEFINE_SYSFS_PROPERTY(fm_rds_mask, unsigned, int, "%u", 0) DEFINE_SYSFS_PROPERTY(fm_best_tune_mode, unsigned, int, "%u", 0) DEFINE_SYSFS_PROPERTY(fm_search_rssi_threshold, unsigned, int, "%u", 0) DEFINE_SYSFS_PROPERTY(fm_search_mode_direction, unsigned, int, "%u", 0) DEFINE_SYSFS_PROPERTY(fm_search_tune_mode, unsigned, int, "%u", value > 3) DEFINE_SYSFS_PROPERTY(rds, unsigned, int, "%u", 0) DEFINE_SYSFS_PROPERTY(rds_b_block_mask, unsigned, int, "%u", 0) DEFINE_SYSFS_PROPERTY(rds_b_block_match, unsigned, int, "%u", 0) DEFINE_SYSFS_PROPERTY(rds_pi_mask, unsigned, int, "%u", 0) DEFINE_SYSFS_PROPERTY(rds_pi_match, unsigned, int, "%u", 0) DEFINE_SYSFS_PROPERTY(rds_wline, unsigned, int, "%u", 0) property_read(rds_pi, unsigned int, "%x") property_str_read(rds_rt, (BCM2048_MAX_RDS_RT + 1)) property_str_read(rds_ps, (BCM2048_MAX_RDS_PS + 1)) property_read(fm_rds_flags, unsigned int, "%u") property_str_read(rds_data, BCM2048_MAX_RDS_RADIO_TEXT * 5) property_read(region_bottom_frequency, unsigned int, "%u") property_read(region_top_frequency, unsigned int, "%u") property_signed_read(fm_carrier_error, int, "%d") property_signed_read(fm_rssi, int, "%d") DEFINE_SYSFS_PROPERTY(region, unsigned, int, "%u", 0) static struct device_attribute attrs[] = { __ATTR(power_state, 0644, bcm2048_power_state_read, bcm2048_power_state_write), __ATTR(mute, 0644, bcm2048_mute_read, bcm2048_mute_write), __ATTR(audio_route, 0644, bcm2048_audio_route_read, bcm2048_audio_route_write), __ATTR(dac_output, 0644, bcm2048_dac_output_read, bcm2048_dac_output_write), __ATTR(fm_hi_lo_injection, 0644, bcm2048_fm_hi_lo_injection_read, bcm2048_fm_hi_lo_injection_write), __ATTR(fm_frequency, 0644, bcm2048_fm_frequency_read, bcm2048_fm_frequency_write), __ATTR(fm_af_frequency, 0644, bcm2048_fm_af_frequency_read, bcm2048_fm_af_frequency_write), __ATTR(fm_deemphasis, 0644, bcm2048_fm_deemphasis_read, bcm2048_fm_deemphasis_write), __ATTR(fm_rds_mask, 0644, bcm2048_fm_rds_mask_read, bcm2048_fm_rds_mask_write), __ATTR(fm_best_tune_mode, 0644, bcm2048_fm_best_tune_mode_read, bcm2048_fm_best_tune_mode_write), __ATTR(fm_search_rssi_threshold, 0644, bcm2048_fm_search_rssi_threshold_read, bcm2048_fm_search_rssi_threshold_write), __ATTR(fm_search_mode_direction, 0644, bcm2048_fm_search_mode_direction_read, bcm2048_fm_search_mode_direction_write), __ATTR(fm_search_tune_mode, 0644, bcm2048_fm_search_tune_mode_read, bcm2048_fm_search_tune_mode_write), __ATTR(rds, 0644, bcm2048_rds_read, bcm2048_rds_write), __ATTR(rds_b_block_mask, 0644, bcm2048_rds_b_block_mask_read, bcm2048_rds_b_block_mask_write), __ATTR(rds_b_block_match, 0644, bcm2048_rds_b_block_match_read, bcm2048_rds_b_block_match_write), __ATTR(rds_pi_mask, 0644, bcm2048_rds_pi_mask_read, bcm2048_rds_pi_mask_write), __ATTR(rds_pi_match, 0644, bcm2048_rds_pi_match_read, bcm2048_rds_pi_match_write), __ATTR(rds_wline, 0644, bcm2048_rds_wline_read, bcm2048_rds_wline_write), __ATTR(rds_pi, 0444, bcm2048_rds_pi_read, NULL), __ATTR(rds_rt, 0444, bcm2048_rds_rt_read, NULL), __ATTR(rds_ps, 0444, bcm2048_rds_ps_read, NULL), __ATTR(fm_rds_flags, 0444, bcm2048_fm_rds_flags_read, NULL), __ATTR(region_bottom_frequency, 0444, bcm2048_region_bottom_frequency_read, NULL), __ATTR(region_top_frequency, 0444, bcm2048_region_top_frequency_read, NULL), __ATTR(fm_carrier_error, 0444, bcm2048_fm_carrier_error_read, NULL), __ATTR(fm_rssi, 0444, bcm2048_fm_rssi_read, NULL), __ATTR(region, 0644, bcm2048_region_read, bcm2048_region_write), __ATTR(rds_data, 0444, bcm2048_rds_data_read, NULL), };
static int bcm2048_sysfs_unregister_properties(struct bcm2048_device *bdev, int size) { int i; for (i = 0; i < size; i++) device_remove_file(&bdev->client->dev, &attrs[i]); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Hans Verkuil49100.00%1100.00%
Total49100.00%1100.00%


static int bcm2048_sysfs_register_properties(struct bcm2048_device *bdev) { int err = 0; int i; for (i = 0; i < ARRAY_SIZE(attrs); i++) { if (device_create_file(&bdev->client->dev, &attrs[i]) != 0) { dev_err(&bdev->client->dev, "could not register sysfs entry\n"); err = -EBUSY; bcm2048_sysfs_unregister_properties(bdev, i); break; } } return err; }

Contributors

PersonTokensPropCommitsCommitProp
Hans Verkuil87100.00%1100.00%
Total87100.00%1100.00%


static int bcm2048_fops_open(struct file *file) { struct bcm2048_device *bdev = video_drvdata(file); bdev->users++; bdev->rd_index = 0; bdev->rds_data_available = 0; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Hans Verkuil41100.00%1100.00%
Total41100.00%1100.00%


static int bcm2048_fops_release(struct file *file) { struct bcm2048_device *bdev = video_drvdata(file); bdev->users--; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Hans Verkuil29100.00%1100.00%
Total29100.00%1100.00%


static unsigned int bcm2048_fops_poll(struct file *file, struct poll_table_struct *pts) { struct bcm2048_device *bdev = video_drvdata(file); int retval = 0; poll_wait(file, &bdev->read_queue, pts); if (bdev->rds_data_available) retval = POLLIN | POLLRDNORM; return retval; }

Contributors

PersonTokensPropCommitsCommitProp
Hans Verkuil59100.00%1100.00%
Total59100.00%1100.00%


static ssize_t bcm2048_fops_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { struct bcm2048_device *bdev = video_drvdata(file); int i; int retval = 0; /* we return at least 3 bytes, one block */ count = (count / 3) * 3; /* only multiples of 3 */ if (count < 3) return -ENOBUFS; while (!bdev->rds_data_available) { if (file->f_flags & O_NONBLOCK) { retval = -EWOULDBLOCK; goto done; } /* interruptible_sleep_on(&bdev->read_queue); */ if (wait_event_interruptible(bdev->read_queue, bdev->rds_data_available) < 0) { retval = -EINTR; goto done; } } mutex_lock(&bdev->mutex); /* copy data to userspace */ i = bdev->fifo_size - bdev->rd_index; if (count > i) count = (i / 3) * 3; i = 0; while (i < count) { unsigned char tmpbuf[3]; tmpbuf[i] = bdev->rds_info.radio_text[bdev->rd_index + i + 2]; tmpbuf[i + 1] = bdev->rds_info.radio_text[bdev->rd_index + i + 1]; tmpbuf[i + 2] = (bdev->rds_info.radio_text[bdev->rd_index + i] & 0xf0) >> 4; if ((bdev->rds_info.radio_text[bdev->rd_index + i] & BCM2048_RDS_CRC_MASK) == BCM2048_RDS_CRC_UNRECOVARABLE) tmpbuf[i + 2] |= 0x80; if (copy_to_user(buf + i, tmpbuf, 3)) { retval = -EFAULT; break; } i += 3; } bdev->rd_index += i; if (bdev->rd_index >= bdev->fifo_size) bdev->rds_data_available = 0; mutex_unlock(&bdev->mutex); if (retval == 0) retval = i; done: return retval; }

Contributors

PersonTokensPropCommitsCommitProp
Hans Verkuil338100.00%1100.00%
Total338100.00%1100.00%

/* * bcm2048_fops - file operations interface */ static const struct v4l2_file_operations bcm2048_fops = { .owner = THIS_MODULE, .unlocked_ioctl = video_ioctl2, /* for RDS read support */ .open = bcm2048_fops_open, .release = bcm2048_fops_release, .read = bcm2048_fops_read, .poll = bcm2048_fops_poll }; /* * Video4Linux Interface */ static struct v4l2_queryctrl bcm2048_v4l2_queryctrl[] = { { .id = V4L2_CID_AUDIO_VOLUME, .flags = V4L2_CTRL_FLAG_DISABLED, }, { .id = V4L2_CID_AUDIO_BALANCE, .flags = V4L2_CTRL_FLAG_DISABLED, }, { .id = V4L2_CID_AUDIO_BASS, .flags = V4L2_CTRL_FLAG_DISABLED, }, { .id = V4L2_CID_AUDIO_TREBLE, .flags = V4L2_CTRL_FLAG_DISABLED, }, { .id = V4L2_CID_AUDIO_MUTE, .type = V4L2_CTRL_TYPE_BOOLEAN, .name = "Mute", .minimum = 0, .maximum = 1, .step = 1, .default_value = 1, }, { .id = V4L2_CID_AUDIO_LOUDNESS, .flags = V4L2_CTRL_FLAG_DISABLED, }, };
static int bcm2048_vidioc_querycap(struct file *file, void *priv, struct v4l2_capability *capability) { struct bcm2048_device *bdev = video_get_drvdata(video_devdata(file)); strlcpy(capability->driver, BCM2048_DRIVER_NAME, sizeof(capability->driver)); strlcpy(capability->card, BCM2048_DRIVER_CARD, sizeof(capability->card)); snprintf(capability->bus_info, 32, "I2C: 0x%X", bdev->client->addr); capability->device_caps = V4L2_CAP_TUNER | V4L2_CAP_RADIO | V4L2_CAP_HW_FREQ_SEEK; capability->capabilities = capability->device_caps | V4L2_CAP_DEVICE_CAPS; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Hans Verkuil105100.00%2100.00%
Total105100.00%2100.00%


static int bcm2048_vidioc_g_input(struct file *filp, void *priv, unsigned int *i) { *i = 0; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Hans Verkuil28100.00%1100.00%
Total28100.00%1100.00%


static int bcm2048_vidioc_s_input(struct file *filp, void *priv, unsigned int i) { if (i) return -EINVAL; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Hans Verkuil30100.00%1100.00%
Total30100.00%1100.00%


static int bcm2048_vidioc_queryctrl(struct file *file, void *priv, struct v4l2_queryctrl *qc) { int i; for (i = 0; i < ARRAY_SIZE(bcm2048_v4l2_queryctrl); i++) { if (qc->id && qc->id == bcm2048_v4l2_queryctrl[i].id) { *qc = bcm2048_v4l2_queryctrl[i]; return 0; } } return -EINVAL; }

Contributors

PersonTokensPropCommitsCommitProp
Hans Verkuil7397.33%150.00%
Fengguang Wu22.67%150.00%
Total75100.00%2100.00%


static int bcm2048_vidioc_g_ctrl(struct file *file, void *priv, struct v4l2_control *ctrl) { struct bcm2048_device *bdev = video_get_drvdata(video_devdata(file)); int err = 0; if (!bdev) return -ENODEV; switch (ctrl->id) { case V4L2_CID_AUDIO_MUTE: err = bcm2048_get_mute(bdev); if (err >= 0) ctrl->value = err; break; } return err; }

Contributors

PersonTokensPropCommitsCommitProp
Hans Verkuil81100.00%1100.00%
Total81100.00%1100.00%


static int bcm2048_vidioc_s_ctrl(struct file *file, void *priv, struct v4l2_control *ctrl) { struct bcm2048_device *bdev = video_get_drvdata(video_devdata(file)); int err = 0; if (!bdev) return -ENODEV; switch (ctrl->id) { case V4L2_CID_AUDIO_MUTE: if (ctrl->value) { if (bdev->power_state) { err = bcm2048_set_mute(bdev, ctrl->value); err |= bcm2048_deinit(bdev); } } else { if (!bdev->power_state) { err = bcm2048_init(bdev); err |= bcm2048_set_mute(bdev, ctrl->value); } } break; } return err; }

Contributors

PersonTokensPropCommitsCommitProp
Hans Verkuil126100.00%1100.00%
Total126100.00%1100.00%


static int bcm2048_vidioc_g_audio(struct file *file, void *priv, struct v4l2_audio *audio) { if (audio->index > 1) return -EINVAL; strncpy(audio->name, "Radio", 32); audio->capability = V4L2_AUDCAP_STEREO; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Hans Verkuil52100.00%1100.00%
Total52100.00%1100.00%


static int bcm2048_vidioc_s_audio(struct file *file, void *priv, const struct v4l2_audio *audio) { if (audio->index != 0) return -EINVAL; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Hans Verkuil36100.00%1100.00%
Total36100.00%1100.00%


static int bcm2048_vidioc_g_tuner(struct file *file, void *priv, struct v4l2_tuner *tuner) { struct bcm2048_device *bdev = video_get_drvdata(video_devdata(file)); s8 f_error; s8 rssi; if (!bdev) return -ENODEV; if (tuner->index > 0) return -EINVAL; strncpy(tuner->name, "FM Receiver", 32); tuner->type = V4L2_TUNER_RADIO; tuner->rangelow = dev_to_v4l2(bcm2048_get_region_bottom_frequency(bdev)); tuner->rangehigh = dev_to_v4l2(bcm2048_get_region_top_frequency(bdev)); tuner->rxsubchans = V4L2_TUNER_SUB_STEREO; tuner->capability = V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_LOW; tuner->audmode = V4L2_TUNER_MODE_STEREO; tuner->afc = 0; if (bdev->power_state) { /* * Report frequencies with high carrier errors to have zero * signal level */ f_error = bcm2048_get_fm_carrier_error(bdev); if (f_error < BCM2048_FREQ_ERROR_FLOOR || f_error > BCM2048_FREQ_ERROR_ROOF) { tuner->signal = 0; } else { /* * RSSI level -60 dB is defined to report full * signal strength */ rssi = bcm2048_get_fm_rssi(bdev); if (rssi >= BCM2048_RSSI_LEVEL_BASE) { tuner->signal = 0xFFFF; } else if (rssi > BCM2048_RSSI_LEVEL_ROOF) { tuner->signal = (rssi + BCM2048_RSSI_LEVEL_ROOF_NEG) * BCM2048_SIGNAL_MULTIPLIER; } else { tuner->signal = 0; } } } else { tuner->signal = 0; } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Hans Verkuil22799.56%150.00%
Carlos E. Garcia10.44%150.00%
Total228100.00%2100.00%


static int bcm2048_vidioc_s_tuner(struct file *file, void *priv, const struct v4l2_tuner *tuner) { struct bcm2048_device *bdev = video_get_drvdata(video_devdata(file)); if (!bdev) return -ENODEV; if (tuner->index > 0) return -EINVAL; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Hans Verkuil58100.00%1100.00%
Total58100.00%1100.00%


static int bcm2048_vidioc_g_frequency(struct file *file, void *priv, struct v4l2_frequency *freq) { struct bcm2048_device *bdev = video_get_drvdata(video_devdata(file)); int err = 0; int f; if (!bdev->power_state) return -ENODEV; freq->type = V4L2_TUNER_RADIO; f = bcm2048_get_fm_frequency(bdev); if (f < 0) err = f; else freq->frequency = dev_to_v4l2(f); return err; }

Contributors

PersonTokensPropCommitsCommitProp
Hans Verkuil88100.00%1100.00%
Total88100.00%1100.00%


static int bcm2048_vidioc_s_frequency(struct file *file, void *priv, const struct v4l2_frequency *freq) { struct bcm2048_device *bdev = video_get_drvdata(video_devdata(file)); int err; if (freq->type != V4L2_TUNER_RADIO) return -EINVAL; if (!bdev->power_state) return -ENODEV; err = bcm2048_set_fm_frequency(bdev, v4l2_to_dev(freq->frequency)); err |= bcm2048_set_fm_search_tune_mode(bdev, BCM2048_FM_PRE_SET_MODE); return err; }

Contributors

PersonTokensPropCommitsCommitProp
Hans Verkuil86100.00%1100.00%
Total86100.00%1100.00%


static int bcm2048_vidioc_s_hw_freq_seek(struct file *file, void *priv, const struct v4l2_hw_freq_seek *seek) { struct bcm2048_device *bdev = video_get_drvdata(video_devdata(file)); int err; if (!bdev->power_state) return -ENODEV; if ((seek->tuner != 0) || (seek->type != V4L2_TUNER_RADIO)) return -EINVAL; err = bcm2048_set_fm_search_mode_direction(bdev, seek->seek_upward); err |= bcm2048_set_fm_search_tune_mode(bdev, BCM2048_FM_AUTO_SEARCH_MODE); return err; }

Contributors

PersonTokensPropCommitsCommitProp
Hans Verkuil93100.00%1100.00%
Total93100.00%1100.00%

static const struct v4l2_ioctl_ops bcm2048_ioctl_ops = { .vidioc_querycap = bcm2048_vidioc_querycap, .vidioc_g_input = bcm2048_vidioc_g_input, .vidioc_s_input = bcm2048_vidioc_s_input, .vidioc_queryctrl = bcm2048_vidioc_queryctrl, .vidioc_g_ctrl = bcm2048_vidioc_g_ctrl, .vidioc_s_ctrl = bcm2048_vidioc_s_ctrl, .vidioc_g_audio = bcm2048_vidioc_g_audio, .vidioc_s_audio = bcm2048_vidioc_s_audio, .vidioc_g_tuner = bcm2048_vidioc_g_tuner, .vidioc_s_tuner = bcm2048_vidioc_s_tuner, .vidioc_g_frequency = bcm2048_vidioc_g_frequency, .vidioc_s_frequency = bcm2048_vidioc_s_frequency, .vidioc_s_hw_freq_seek = bcm2048_vidioc_s_hw_freq_seek, }; /* * bcm2048_viddev_template - video device interface */ static struct video_device bcm2048_viddev_template = { .fops = &bcm2048_fops, .name = BCM2048_DRIVER_NAME, .release = video_device_release_empty, .ioctl_ops = &bcm2048_ioctl_ops, }; /* * I2C driver interface */
static int bcm2048_i2c_driver_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct bcm2048_device *bdev; int err; bdev = kzalloc(sizeof(*bdev), GFP_KERNEL); if (!bdev) { err = -ENOMEM; goto exit; } bdev->client = client; i2c_set_clientdata(client, bdev); mutex_init(&bdev->mutex); init_completion(&bdev->compl); INIT_WORK(&bdev->work, bcm2048_work); if (client->irq) { err = request_irq(client->irq, bcm2048_handler, IRQF_TRIGGER_FALLING, client->name, bdev); if (err < 0) { dev_err(&client->dev, "Could not request IRQ\n"); goto free_bdev; } dev_dbg(&client->dev, "IRQ requested.\n"); } else { dev_dbg(&client->dev, "IRQ not configured. Using timeouts.\n"); } bdev->videodev = bcm2048_viddev_template; video_set_drvdata(&bdev->videodev, bdev); if (video_register_device(&bdev->videodev, VFL_TYPE_RADIO, radio_nr)) { dev_dbg(&client->dev, "Could not register video device.\n"); err = -EIO; goto free_irq; } err = bcm2048_sysfs_register_properties(bdev); if (err < 0) { dev_dbg(&client->dev, "Could not register sysfs interface.\n"); goto free_registration; } err = bcm2048_probe(bdev); if (err < 0) { dev_dbg(&client->dev, "Failed to probe device information.\n"); goto free_sysfs; } return 0; free_sysfs: bcm2048_sysfs_unregister_properties(bdev, ARRAY_SIZE(attrs)); free_registration: video_unregister_device(&bdev->videodev); free_irq: if (client->irq) free_irq(client->irq, bdev); free_bdev: i2c_set_clientdata(client, NULL); kfree(bdev); exit: return err; }

Contributors

PersonTokensPropCommitsCommitProp
Hans Verkuil32999.70%266.67%
Fengguang Wu10.30%133.33%
Total330100.00%3100.00%


static int __exit bcm2048_i2c_driver_remove(struct i2c_client *client) { struct bcm2048_device *bdev = i2c_get_clientdata(client); if (!client->adapter) return -ENODEV; if (bdev) { bcm2048_sysfs_unregister_properties(bdev, ARRAY_SIZE(attrs)); video_unregister_device(&bdev->videodev); if (bdev->power_state) bcm2048_set_power_state(bdev, BCM2048_POWER_OFF); if (client->irq > 0) free_irq(client->irq, bdev); cancel_work_sync(&bdev->work); kfree(bdev); } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Hans Verkuil103100.00%2100.00%
Total103100.00%2100.00%

/* * bcm2048_i2c_driver - i2c driver interface */ static const struct i2c_device_id bcm2048_id[] = { { "bcm2048", 0 }, { }, }; MODULE_DEVICE_TABLE(i2c, bcm2048_id); static struct i2c_driver bcm2048_i2c_driver = { .driver = { .name = BCM2048_DRIVER_NAME, }, .probe = bcm2048_i2c_driver_probe, .remove = __exit_p(bcm2048_i2c_driver_remove), .id_table = bcm2048_id, }; module_i2c_driver(bcm2048_i2c_driver); MODULE_LICENSE("GPL"); MODULE_AUTHOR(BCM2048_DRIVER_AUTHOR); MODULE_DESCRIPTION(BCM2048_DRIVER_DESC); MODULE_VERSION("0.0.2");

Overall Contributors

PersonTokensPropCommitsCommitProp
Hans Verkuil1074798.46%415.38%
Jan Roemisch550.50%13.85%
Andrew Milkovich380.35%13.85%
Jean-Baptiste Abbadie300.27%27.69%
Mauro Carvalho Chehab80.07%27.69%
Wei Yongjun80.07%13.85%
Sandhya Bankar60.05%13.85%
Amarjargal Gundjalam50.05%311.54%
Fengguang Wu40.04%13.85%
Rehas Sachdeva30.03%13.85%
Navya Sri Nizamkari20.02%13.85%
Carlos E. Garcia20.02%13.85%
Claudiu Beznea10.01%13.85%
Ran Algawi10.01%13.85%
Vaishali Thakkar10.01%13.85%
Pali Rohár10.01%13.85%
Luis de Bethencourt10.01%13.85%
Bhumika Goyal10.01%13.85%
Luke Hart10.01%13.85%
Total10915100.00%26100.00%
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with cregit.