2018-08-22 05:00:09 +03:00
// SPDX-License-Identifier: GPL-2.0
2017-06-13 15:54:47 +03:00
/*
* Maxim Integrated MAX2175 RF to Bits tuner driver
*
* This driver & most of the hard coded values are based on the reference
* application delivered by Maxim for this device .
*
* Copyright ( C ) 2016 Maxim Integrated Products
* Copyright ( C ) 2017 Renesas Electronics Corporation
*/
# include <linux/clk.h>
# include <linux/delay.h>
# include <linux/errno.h>
# include <linux/i2c.h>
# include <linux/kernel.h>
# include <linux/math64.h>
# include <linux/max2175.h>
# include <linux/module.h>
# include <linux/of.h>
# include <linux/regmap.h>
# include <linux/slab.h>
# include <media/v4l2-ctrls.h>
# include <media/v4l2-device.h>
# include "max2175.h"
# define DRIVER_NAME "max2175"
# define mxm_dbg(ctx, fmt, arg...) dev_dbg(&ctx->client->dev, fmt, ## arg)
# define mxm_err(ctx, fmt, arg...) dev_err(&ctx->client->dev, fmt, ## arg)
/* Rx mode */
struct max2175_rxmode {
enum max2175_band band ; /* Associated band */
u32 freq ; /* Default freq in Hz */
u8 i2s_word_size ; /* Bit value */
} ;
/* Register map to define preset values */
struct max2175_reg_map {
u8 idx ; /* Register index */
u8 val ; /* Register value */
} ;
static const struct max2175_rxmode eu_rx_modes [ ] = {
/* EU modes */
[ MAX2175_EU_FM_1_2 ] = { MAX2175_BAND_FM , 98256000 , 1 } ,
[ MAX2175_DAB_1_2 ] = { MAX2175_BAND_VHF , 182640000 , 0 } ,
} ;
static const struct max2175_rxmode na_rx_modes [ ] = {
/* NA modes */
[ MAX2175_NA_FM_1_0 ] = { MAX2175_BAND_FM , 98255520 , 1 } ,
[ MAX2175_NA_FM_2_0 ] = { MAX2175_BAND_FM , 98255520 , 6 } ,
} ;
/*
* Preset values :
* Based on Maxim MAX2175 Register Table revision : 130 p10
*/
static const u8 full_fm_eu_1p0 [ ] = {
0x15 , 0x04 , 0xb8 , 0xe3 , 0x35 , 0x18 , 0x7c , 0x00 ,
0x00 , 0x7d , 0x40 , 0x08 , 0x70 , 0x7a , 0x88 , 0x91 ,
0x61 , 0x61 , 0x61 , 0x61 , 0x5a , 0x0f , 0x34 , 0x1c ,
0x14 , 0x88 , 0x33 , 0x02 , 0x00 , 0x09 , 0x00 , 0x65 ,
0x9f , 0x2b , 0x80 , 0x00 , 0x95 , 0x05 , 0x2c , 0x00 ,
0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x40 ,
0x4a , 0x08 , 0xa8 , 0x0e , 0x0e , 0x2f , 0x7e , 0x00 ,
0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ,
0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0xab , 0x5e , 0xa9 ,
0xae , 0xbb , 0x57 , 0x18 , 0x3b , 0x03 , 0x3b , 0x64 ,
0x40 , 0x60 , 0x00 , 0x2a , 0xbf , 0x3f , 0xff , 0x9f ,
0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x0a , 0x00 ,
0xff , 0xfc , 0xef , 0x1c , 0x40 , 0x00 , 0x00 , 0x02 ,
0x00 , 0x00 , 0xe0 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ,
0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0xac , 0x40 , 0x00 ,
0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x75 , 0x00 , 0x00 ,
0x00 , 0x47 , 0x00 , 0x00 , 0x11 , 0x3f , 0x22 , 0x00 ,
0xf1 , 0x00 , 0x41 , 0x03 , 0xb0 , 0x00 , 0x00 , 0x00 ,
0x1b ,
} ;
static const u8 full_fm_na_1p0 [ ] = {
0x13 , 0x08 , 0x8d , 0xc0 , 0x35 , 0x18 , 0x7d , 0x3f ,
0x7d , 0x75 , 0x40 , 0x08 , 0x70 , 0x7a , 0x88 , 0x91 ,
0x61 , 0x61 , 0x61 , 0x61 , 0x5c , 0x0f , 0x34 , 0x1c ,
0x14 , 0x88 , 0x33 , 0x02 , 0x00 , 0x01 , 0x00 , 0x65 ,
0x9f , 0x2b , 0x80 , 0x00 , 0x95 , 0x05 , 0x2c , 0x00 ,
0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x40 ,
0x4a , 0x08 , 0xa8 , 0x0e , 0x0e , 0xaf , 0x7e , 0x00 ,
0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ,
0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0xab , 0x5e , 0xa9 ,
0xae , 0xbb , 0x57 , 0x18 , 0x3b , 0x03 , 0x3b , 0x64 ,
0x40 , 0x60 , 0x00 , 0x2a , 0xbf , 0x3f , 0xff , 0x9f ,
0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x0a , 0x00 ,
0xff , 0xfc , 0xef , 0x1c , 0x40 , 0x00 , 0x00 , 0x02 ,
0x00 , 0x00 , 0xe0 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ,
0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0xa6 , 0x40 , 0x00 ,
0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x75 , 0x00 , 0x00 ,
0x00 , 0x35 , 0x00 , 0x00 , 0x11 , 0x3f , 0x22 , 0x00 ,
0xf1 , 0x00 , 0x41 , 0x03 , 0xb0 , 0x00 , 0x00 , 0x00 ,
0x1b ,
} ;
/* DAB1.2 settings */
static const struct max2175_reg_map dab12_map [ ] = {
{ 0x01 , 0x13 } , { 0x02 , 0x0d } , { 0x03 , 0x15 } , { 0x04 , 0x55 } ,
{ 0x05 , 0x0a } , { 0x06 , 0xa0 } , { 0x07 , 0x40 } , { 0x08 , 0x00 } ,
{ 0x09 , 0x00 } , { 0x0a , 0x7d } , { 0x0b , 0x4a } , { 0x0c , 0x28 } ,
{ 0x0e , 0x43 } , { 0x0f , 0xb5 } , { 0x10 , 0x31 } , { 0x11 , 0x9e } ,
{ 0x12 , 0x68 } , { 0x13 , 0x9e } , { 0x14 , 0x68 } , { 0x15 , 0x58 } ,
{ 0x16 , 0x2f } , { 0x17 , 0x3f } , { 0x18 , 0x40 } , { 0x1a , 0x88 } ,
{ 0x1b , 0xaa } , { 0x1c , 0x9a } , { 0x1d , 0x00 } , { 0x1e , 0x00 } ,
{ 0x23 , 0x80 } , { 0x24 , 0x00 } , { 0x25 , 0x00 } , { 0x26 , 0x00 } ,
{ 0x27 , 0x00 } , { 0x32 , 0x08 } , { 0x33 , 0xf8 } , { 0x36 , 0x2d } ,
{ 0x37 , 0x7e } , { 0x55 , 0xaf } , { 0x56 , 0x3f } , { 0x57 , 0xf8 } ,
{ 0x58 , 0x99 } , { 0x76 , 0x00 } , { 0x77 , 0x00 } , { 0x78 , 0x02 } ,
{ 0x79 , 0x40 } , { 0x82 , 0x00 } , { 0x83 , 0x00 } , { 0x85 , 0x00 } ,
{ 0x86 , 0x20 } ,
} ;
/* EU FM 1.2 settings */
static const struct max2175_reg_map fmeu1p2_map [ ] = {
{ 0x01 , 0x15 } , { 0x02 , 0x04 } , { 0x03 , 0xb8 } , { 0x04 , 0xe3 } ,
{ 0x05 , 0x35 } , { 0x06 , 0x18 } , { 0x07 , 0x7c } , { 0x08 , 0x00 } ,
{ 0x09 , 0x00 } , { 0x0a , 0x73 } , { 0x0b , 0x40 } , { 0x0c , 0x08 } ,
{ 0x0e , 0x7a } , { 0x0f , 0x88 } , { 0x10 , 0x91 } , { 0x11 , 0x61 } ,
{ 0x12 , 0x61 } , { 0x13 , 0x61 } , { 0x14 , 0x61 } , { 0x15 , 0x5a } ,
{ 0x16 , 0x0f } , { 0x17 , 0x34 } , { 0x18 , 0x1c } , { 0x1a , 0x88 } ,
{ 0x1b , 0x33 } , { 0x1c , 0x02 } , { 0x1d , 0x00 } , { 0x1e , 0x01 } ,
{ 0x23 , 0x80 } , { 0x24 , 0x00 } , { 0x25 , 0x95 } , { 0x26 , 0x05 } ,
{ 0x27 , 0x2c } , { 0x32 , 0x08 } , { 0x33 , 0xa8 } , { 0x36 , 0x2f } ,
{ 0x37 , 0x7e } , { 0x55 , 0xbf } , { 0x56 , 0x3f } , { 0x57 , 0xff } ,
{ 0x58 , 0x9f } , { 0x76 , 0xac } , { 0x77 , 0x40 } , { 0x78 , 0x00 } ,
{ 0x79 , 0x00 } , { 0x82 , 0x47 } , { 0x83 , 0x00 } , { 0x85 , 0x11 } ,
{ 0x86 , 0x3f } ,
} ;
/* FM NA 1.0 settings */
static const struct max2175_reg_map fmna1p0_map [ ] = {
{ 0x01 , 0x13 } , { 0x02 , 0x08 } , { 0x03 , 0x8d } , { 0x04 , 0xc0 } ,
{ 0x05 , 0x35 } , { 0x06 , 0x18 } , { 0x07 , 0x7d } , { 0x08 , 0x3f } ,
{ 0x09 , 0x7d } , { 0x0a , 0x75 } , { 0x0b , 0x40 } , { 0x0c , 0x08 } ,
{ 0x0e , 0x7a } , { 0x0f , 0x88 } , { 0x10 , 0x91 } , { 0x11 , 0x61 } ,
{ 0x12 , 0x61 } , { 0x13 , 0x61 } , { 0x14 , 0x61 } , { 0x15 , 0x5c } ,
{ 0x16 , 0x0f } , { 0x17 , 0x34 } , { 0x18 , 0x1c } , { 0x1a , 0x88 } ,
{ 0x1b , 0x33 } , { 0x1c , 0x02 } , { 0x1d , 0x00 } , { 0x1e , 0x01 } ,
{ 0x23 , 0x80 } , { 0x24 , 0x00 } , { 0x25 , 0x95 } , { 0x26 , 0x05 } ,
{ 0x27 , 0x2c } , { 0x32 , 0x08 } , { 0x33 , 0xa8 } , { 0x36 , 0xaf } ,
{ 0x37 , 0x7e } , { 0x55 , 0xbf } , { 0x56 , 0x3f } , { 0x57 , 0xff } ,
{ 0x58 , 0x9f } , { 0x76 , 0xa6 } , { 0x77 , 0x40 } , { 0x78 , 0x00 } ,
{ 0x79 , 0x00 } , { 0x82 , 0x35 } , { 0x83 , 0x00 } , { 0x85 , 0x11 } ,
{ 0x86 , 0x3f } ,
} ;
/* FM NA 2.0 settings */
static const struct max2175_reg_map fmna2p0_map [ ] = {
{ 0x01 , 0x13 } , { 0x02 , 0x08 } , { 0x03 , 0x8d } , { 0x04 , 0xc0 } ,
{ 0x05 , 0x35 } , { 0x06 , 0x18 } , { 0x07 , 0x7c } , { 0x08 , 0x54 } ,
{ 0x09 , 0xa7 } , { 0x0a , 0x55 } , { 0x0b , 0x42 } , { 0x0c , 0x48 } ,
{ 0x0e , 0x7a } , { 0x0f , 0x88 } , { 0x10 , 0x91 } , { 0x11 , 0x61 } ,
{ 0x12 , 0x61 } , { 0x13 , 0x61 } , { 0x14 , 0x61 } , { 0x15 , 0x5c } ,
{ 0x16 , 0x0f } , { 0x17 , 0x34 } , { 0x18 , 0x1c } , { 0x1a , 0x88 } ,
{ 0x1b , 0x33 } , { 0x1c , 0x02 } , { 0x1d , 0x00 } , { 0x1e , 0x01 } ,
{ 0x23 , 0x80 } , { 0x24 , 0x00 } , { 0x25 , 0x95 } , { 0x26 , 0x05 } ,
{ 0x27 , 0x2c } , { 0x32 , 0x08 } , { 0x33 , 0xa8 } , { 0x36 , 0xaf } ,
{ 0x37 , 0x7e } , { 0x55 , 0xbf } , { 0x56 , 0x3f } , { 0x57 , 0xff } ,
{ 0x58 , 0x9f } , { 0x76 , 0xac } , { 0x77 , 0xc0 } , { 0x78 , 0x00 } ,
{ 0x79 , 0x00 } , { 0x82 , 0x6b } , { 0x83 , 0x00 } , { 0x85 , 0x11 } ,
{ 0x86 , 0x3f } ,
} ;
static const u16 ch_coeff_dab1 [ ] = {
0x001c , 0x0007 , 0xffcd , 0x0056 , 0xffa4 , 0x0033 , 0x0027 , 0xff61 ,
0x010e , 0xfec0 , 0x0106 , 0xffb8 , 0xff1c , 0x023c , 0xfcb2 , 0x039b ,
0xfd4e , 0x0055 , 0x036a , 0xf7de , 0x0d21 , 0xee72 , 0x1499 , 0x6a51 ,
} ;
static const u16 ch_coeff_fmeu [ ] = {
0x0000 , 0xffff , 0x0001 , 0x0002 , 0xfffa , 0xffff , 0x0015 , 0xffec ,
0xffde , 0x0054 , 0xfff9 , 0xff52 , 0x00b8 , 0x00a2 , 0xfe0a , 0x00af ,
0x02e3 , 0xfc14 , 0xfe89 , 0x089d , 0xfa2e , 0xf30f , 0x25be , 0x4eb6 ,
} ;
static const u16 eq_coeff_fmeu1_ra02_m6db [ ] = {
0x0040 , 0xffc6 , 0xfffa , 0x002c , 0x000d , 0xff90 , 0x0037 , 0x006e ,
0xffc0 , 0xff5b , 0x006a , 0x00f0 , 0xff57 , 0xfe94 , 0x0112 , 0x0252 ,
0xfe0c , 0xfc6a , 0x0385 , 0x0553 , 0xfa49 , 0xf789 , 0x0b91 , 0x1a10 ,
} ;
static const u16 ch_coeff_fmna [ ] = {
0x0001 , 0x0003 , 0xfffe , 0xfff4 , 0x0000 , 0x001f , 0x000c , 0xffbc ,
0xffd3 , 0x007d , 0x0075 , 0xff33 , 0xff01 , 0x0131 , 0x01ef , 0xfe60 ,
0xfc7a , 0x020e , 0x0656 , 0xfd94 , 0xf395 , 0x02ab , 0x2857 , 0x3d3f ,
} ;
static const u16 eq_coeff_fmna1_ra02_m6db [ ] = {
0xfff1 , 0xffe1 , 0xffef , 0x000e , 0x0030 , 0x002f , 0xfff6 , 0xffa7 ,
0xff9d , 0x000a , 0x00a2 , 0x00b5 , 0xffea , 0xfed9 , 0xfec5 , 0x003d ,
0x0217 , 0x021b , 0xff5a , 0xfc2b , 0xfcbd , 0x02c4 , 0x0ac3 , 0x0e85 ,
} ;
static const u8 adc_presets [ 2 ] [ 23 ] = {
{
0x83 , 0x00 , 0xcf , 0xb4 , 0x0f , 0x2c , 0x0c , 0x49 ,
0x00 , 0x00 , 0x00 , 0x8c , 0x02 , 0x02 , 0x00 , 0x04 ,
0xec , 0x82 , 0x4b , 0xcc , 0x01 , 0x88 , 0x0c ,
} ,
{
0x83 , 0x00 , 0xcf , 0xb4 , 0x0f , 0x2c , 0x0c , 0x49 ,
0x00 , 0x00 , 0x00 , 0x8c , 0x02 , 0x20 , 0x33 , 0x8c ,
0x57 , 0xd7 , 0x59 , 0xb7 , 0x65 , 0x0e , 0x0c ,
} ,
} ;
/* Tuner bands */
static const struct v4l2_frequency_band eu_bands_rf = {
. tuner = 0 ,
. type = V4L2_TUNER_RF ,
. index = 0 ,
. capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS ,
. rangelow = 65000000 ,
. rangehigh = 240000000 ,
} ;
static const struct v4l2_frequency_band na_bands_rf = {
. tuner = 0 ,
. type = V4L2_TUNER_RF ,
. index = 0 ,
. capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS ,
. rangelow = 65000000 ,
. rangehigh = 108000000 ,
} ;
/* Regmap settings */
static const struct regmap_range max2175_regmap_volatile_range [ ] = {
regmap_reg_range ( 0x30 , 0x35 ) ,
regmap_reg_range ( 0x3a , 0x45 ) ,
regmap_reg_range ( 0x59 , 0x5e ) ,
regmap_reg_range ( 0x73 , 0x75 ) ,
} ;
static const struct regmap_access_table max2175_volatile_regs = {
. yes_ranges = max2175_regmap_volatile_range ,
. n_yes_ranges = ARRAY_SIZE ( max2175_regmap_volatile_range ) ,
} ;
static const struct reg_default max2175_reg_defaults [ ] = {
{ 0x00 , 0x07 } ,
} ;
static const struct regmap_config max2175_regmap_config = {
. reg_bits = 8 ,
. val_bits = 8 ,
. max_register = 0xff ,
. reg_defaults = max2175_reg_defaults ,
. num_reg_defaults = ARRAY_SIZE ( max2175_reg_defaults ) ,
. volatile_table = & max2175_volatile_regs ,
2022-02-23 00:52:44 +03:00
. cache_type = REGCACHE_RBTREE ,
2017-06-13 15:54:47 +03:00
} ;
struct max2175 {
struct v4l2_subdev sd ; /* Sub-device */
struct i2c_client * client ; /* I2C client */
/* Controls */
struct v4l2_ctrl_handler ctrl_hdl ;
struct v4l2_ctrl * lna_gain ; /* LNA gain value */
struct v4l2_ctrl * if_gain ; /* I/F gain value */
struct v4l2_ctrl * pll_lock ; /* PLL lock */
struct v4l2_ctrl * i2s_en ; /* I2S output enable */
struct v4l2_ctrl * hsls ; /* High-side/Low-side polarity */
struct v4l2_ctrl * rx_mode ; /* Receive mode */
/* Regmap */
struct regmap * regmap ;
/* Cached configuration */
u32 freq ; /* Tuned freq In Hz */
const struct max2175_rxmode * rx_modes ; /* EU or NA modes */
const struct v4l2_frequency_band * bands_rf ; /* EU or NA bands */
/* Device settings */
unsigned long xtal_freq ; /* Ref Oscillator freq in Hz */
u32 decim_ratio ;
bool master ; /* Master/Slave */
bool am_hiz ; /* AM Hi-Z filter */
/* ROM values */
u8 rom_bbf_bw_am ;
u8 rom_bbf_bw_fm ;
u8 rom_bbf_bw_dab ;
/* Driver private variables */
bool mode_resolved ; /* Flag to sanity check settings */
} ;
static inline struct max2175 * max2175_from_sd ( struct v4l2_subdev * sd )
{
return container_of ( sd , struct max2175 , sd ) ;
}
static inline struct max2175 * max2175_from_ctrl_hdl ( struct v4l2_ctrl_handler * h )
{
return container_of ( h , struct max2175 , ctrl_hdl ) ;
}
/* Get bitval of a given val */
static inline u8 max2175_get_bitval ( u8 val , u8 msb , u8 lsb )
{
return ( val & GENMASK ( msb , lsb ) ) > > lsb ;
}
/* Read/Write bit(s) on top of regmap */
static int max2175_read ( struct max2175 * ctx , u8 idx , u8 * val )
{
u32 regval ;
int ret ;
ret = regmap_read ( ctx - > regmap , idx , & regval ) ;
if ( ret )
mxm_err ( ctx , " read ret(%d): idx 0x%02x \n " , ret , idx ) ;
else
* val = regval ;
return ret ;
}
static int max2175_write ( struct max2175 * ctx , u8 idx , u8 val )
{
int ret ;
ret = regmap_write ( ctx - > regmap , idx , val ) ;
if ( ret )
mxm_err ( ctx , " write ret(%d): idx 0x%02x val 0x%02x \n " ,
ret , idx , val ) ;
return ret ;
}
static u8 max2175_read_bits ( struct max2175 * ctx , u8 idx , u8 msb , u8 lsb )
{
u8 val ;
if ( max2175_read ( ctx , idx , & val ) )
return 0 ;
return max2175_get_bitval ( val , msb , lsb ) ;
}
static int max2175_write_bits ( struct max2175 * ctx , u8 idx ,
u8 msb , u8 lsb , u8 newval )
{
int ret = regmap_update_bits ( ctx - > regmap , idx , GENMASK ( msb , lsb ) ,
newval < < lsb ) ;
if ( ret )
mxm_err ( ctx , " wbits ret(%d): idx 0x%02x \n " , ret , idx ) ;
return ret ;
}
static int max2175_write_bit ( struct max2175 * ctx , u8 idx , u8 bit , u8 newval )
{
return max2175_write_bits ( ctx , idx , bit , bit , newval ) ;
}
/* Checks expected pattern every msec until timeout */
static int max2175_poll_timeout ( struct max2175 * ctx , u8 idx , u8 msb , u8 lsb ,
u8 exp_bitval , u32 timeout_us )
{
unsigned int val ;
return regmap_read_poll_timeout ( ctx - > regmap , idx , val ,
( max2175_get_bitval ( val , msb , lsb ) = = exp_bitval ) ,
1000 , timeout_us ) ;
}
static int max2175_poll_csm_ready ( struct max2175 * ctx )
{
int ret ;
ret = max2175_poll_timeout ( ctx , 69 , 1 , 1 , 0 , 50000 ) ;
if ( ret )
mxm_err ( ctx , " csm not ready \n " ) ;
return ret ;
}
# define MAX2175_IS_BAND_AM(ctx) \
( max2175_read_bits ( ctx , 5 , 1 , 0 ) = = MAX2175_BAND_AM )
# define MAX2175_IS_BAND_VHF(ctx) \
( max2175_read_bits ( ctx , 5 , 1 , 0 ) = = MAX2175_BAND_VHF )
# define MAX2175_IS_FM_MODE(ctx) \
( max2175_read_bits ( ctx , 12 , 5 , 4 ) = = 0 )
# define MAX2175_IS_FMHD_MODE(ctx) \
( max2175_read_bits ( ctx , 12 , 5 , 4 ) = = 1 )
# define MAX2175_IS_DAB_MODE(ctx) \
( max2175_read_bits ( ctx , 12 , 5 , 4 ) = = 2 )
static int max2175_band_from_freq ( u32 freq )
{
if ( freq > = 144000 & & freq < = 26100000 )
return MAX2175_BAND_AM ;
else if ( freq > = 65000000 & & freq < = 108000000 )
return MAX2175_BAND_FM ;
return MAX2175_BAND_VHF ;
}
static void max2175_i2s_enable ( struct max2175 * ctx , bool enable )
{
if ( enable )
/* Stuff bits are zeroed */
max2175_write_bits ( ctx , 104 , 3 , 0 , 2 ) ;
else
/* Keep SCK alive */
max2175_write_bits ( ctx , 104 , 3 , 0 , 9 ) ;
mxm_dbg ( ctx , " i2s %sabled \n " , enable ? " en " : " dis " ) ;
}
static void max2175_set_filter_coeffs ( struct max2175 * ctx , u8 m_sel ,
u8 bank , const u16 * coeffs )
{
unsigned int i ;
u8 coeff_addr , upper_address = 24 ;
mxm_dbg ( ctx , " set_filter_coeffs: m_sel %d bank %d \n " , m_sel , bank ) ;
max2175_write_bits ( ctx , 114 , 5 , 4 , m_sel ) ;
if ( m_sel = = 2 )
upper_address = 12 ;
for ( i = 0 ; i < upper_address ; i + + ) {
coeff_addr = i + bank * 24 ;
max2175_write ( ctx , 115 , coeffs [ i ] > > 8 ) ;
max2175_write ( ctx , 116 , coeffs [ i ] ) ;
max2175_write ( ctx , 117 , coeff_addr | 1 < < 7 ) ;
}
max2175_write_bit ( ctx , 117 , 7 , 0 ) ;
}
static void max2175_load_fmeu_1p2 ( struct max2175 * ctx )
{
unsigned int i ;
for ( i = 0 ; i < ARRAY_SIZE ( fmeu1p2_map ) ; i + + )
max2175_write ( ctx , fmeu1p2_map [ i ] . idx , fmeu1p2_map [ i ] . val ) ;
ctx - > decim_ratio = 36 ;
/* Load the Channel Filter Coefficients into channel filter bank #2 */
max2175_set_filter_coeffs ( ctx , MAX2175_CH_MSEL , 0 , ch_coeff_fmeu ) ;
max2175_set_filter_coeffs ( ctx , MAX2175_EQ_MSEL , 0 ,
eq_coeff_fmeu1_ra02_m6db ) ;
}
static void max2175_load_dab_1p2 ( struct max2175 * ctx )
{
unsigned int i ;
for ( i = 0 ; i < ARRAY_SIZE ( dab12_map ) ; i + + )
max2175_write ( ctx , dab12_map [ i ] . idx , dab12_map [ i ] . val ) ;
ctx - > decim_ratio = 1 ;
/* Load the Channel Filter Coefficients into channel filter bank #2 */
max2175_set_filter_coeffs ( ctx , MAX2175_CH_MSEL , 2 , ch_coeff_dab1 ) ;
}
static void max2175_load_fmna_1p0 ( struct max2175 * ctx )
{
unsigned int i ;
for ( i = 0 ; i < ARRAY_SIZE ( fmna1p0_map ) ; i + + )
max2175_write ( ctx , fmna1p0_map [ i ] . idx , fmna1p0_map [ i ] . val ) ;
}
static void max2175_load_fmna_2p0 ( struct max2175 * ctx )
{
unsigned int i ;
for ( i = 0 ; i < ARRAY_SIZE ( fmna2p0_map ) ; i + + )
max2175_write ( ctx , fmna2p0_map [ i ] . idx , fmna2p0_map [ i ] . val ) ;
}
static void max2175_set_bbfilter ( struct max2175 * ctx )
{
if ( MAX2175_IS_BAND_AM ( ctx ) ) {
max2175_write_bits ( ctx , 12 , 3 , 0 , ctx - > rom_bbf_bw_am ) ;
mxm_dbg ( ctx , " set_bbfilter AM: rom %d \n " , ctx - > rom_bbf_bw_am ) ;
} else if ( MAX2175_IS_DAB_MODE ( ctx ) ) {
max2175_write_bits ( ctx , 12 , 3 , 0 , ctx - > rom_bbf_bw_dab ) ;
mxm_dbg ( ctx , " set_bbfilter DAB: rom %d \n " , ctx - > rom_bbf_bw_dab ) ;
} else {
max2175_write_bits ( ctx , 12 , 3 , 0 , ctx - > rom_bbf_bw_fm ) ;
mxm_dbg ( ctx , " set_bbfilter FM: rom %d \n " , ctx - > rom_bbf_bw_fm ) ;
}
}
2020-03-04 17:23:12 +03:00
static int max2175_set_csm_mode ( struct max2175 * ctx ,
2017-06-13 15:54:47 +03:00
enum max2175_csm_mode new_mode )
{
int ret = max2175_poll_csm_ready ( ctx ) ;
if ( ret )
return ret ;
max2175_write_bits ( ctx , 0 , 2 , 0 , new_mode ) ;
mxm_dbg ( ctx , " set csm new mode %d \n " , new_mode ) ;
/* Wait for a fixed settle down time depending on new mode */
switch ( new_mode ) {
case MAX2175_PRESET_TUNE :
usleep_range ( 51100 , 51500 ) ; /* 51.1ms */
break ;
/*
* Other mode switches need different sleep values depending on band &
* mode
*/
default :
break ;
}
return max2175_poll_csm_ready ( ctx ) ;
}
static int max2175_csm_action ( struct max2175 * ctx ,
enum max2175_csm_mode action )
{
int ret ;
mxm_dbg ( ctx , " csm_action: %d \n " , action ) ;
/* Other actions can be added in future when needed */
ret = max2175_set_csm_mode ( ctx , MAX2175_LOAD_TO_BUFFER ) ;
if ( ret )
return ret ;
return max2175_set_csm_mode ( ctx , MAX2175_PRESET_TUNE ) ;
}
static int max2175_set_lo_freq ( struct max2175 * ctx , u32 lo_freq )
{
u8 lo_mult , loband_bits = 0 , vcodiv_bits = 0 ;
u32 int_desired , frac_desired ;
enum max2175_band band ;
int ret ;
band = max2175_read_bits ( ctx , 5 , 1 , 0 ) ;
switch ( band ) {
case MAX2175_BAND_AM :
lo_mult = 16 ;
break ;
case MAX2175_BAND_FM :
if ( lo_freq < = 74700000 ) {
lo_mult = 16 ;
} else if ( lo_freq > 74700000 & & lo_freq < = 110000000 ) {
loband_bits = 1 ;
lo_mult = 8 ;
} else {
loband_bits = 1 ;
vcodiv_bits = 3 ;
lo_mult = 8 ;
}
break ;
case MAX2175_BAND_VHF :
if ( lo_freq < = 210000000 )
vcodiv_bits = 2 ;
else
vcodiv_bits = 1 ;
loband_bits = 2 ;
lo_mult = 4 ;
break ;
default :
loband_bits = 3 ;
vcodiv_bits = 2 ;
lo_mult = 2 ;
break ;
}
if ( band = = MAX2175_BAND_L )
lo_freq / = lo_mult ;
else
lo_freq * = lo_mult ;
int_desired = lo_freq / ctx - > xtal_freq ;
2019-08-30 16:48:27 +03:00
frac_desired = div64_ul ( ( u64 ) ( lo_freq % ctx - > xtal_freq ) < < 20 ,
ctx - > xtal_freq ) ;
2017-06-13 15:54:47 +03:00
/* Check CSM is not busy */
ret = max2175_poll_csm_ready ( ctx ) ;
if ( ret )
return ret ;
mxm_dbg ( ctx , " lo_mult %u int %u frac %u \n " ,
lo_mult , int_desired , frac_desired ) ;
/* Write the calculated values to the appropriate registers */
max2175_write ( ctx , 1 , int_desired ) ;
max2175_write_bits ( ctx , 2 , 3 , 0 , ( frac_desired > > 16 ) & 0xf ) ;
max2175_write ( ctx , 3 , frac_desired > > 8 ) ;
max2175_write ( ctx , 4 , frac_desired ) ;
max2175_write_bits ( ctx , 5 , 3 , 2 , loband_bits ) ;
max2175_write_bits ( ctx , 6 , 7 , 6 , vcodiv_bits ) ;
return ret ;
}
/*
* Helper similar to DIV_ROUND_CLOSEST but an inline function that accepts s64
* dividend and s32 divisor
*/
static inline s64 max2175_round_closest ( s64 dividend , s32 divisor )
{
if ( ( dividend > 0 & & divisor > 0 ) | | ( dividend < 0 & & divisor < 0 ) )
return div_s64 ( dividend + divisor / 2 , divisor ) ;
return div_s64 ( dividend - divisor / 2 , divisor ) ;
}
static int max2175_set_nco_freq ( struct max2175 * ctx , s32 nco_freq )
{
s32 clock_rate = ctx - > xtal_freq / ctx - > decim_ratio ;
u32 nco_reg , abs_nco_freq = abs ( nco_freq ) ;
s64 nco_val_desired ;
int ret ;
if ( abs_nco_freq < clock_rate / 2 ) {
nco_val_desired = 2 * nco_freq ;
} else {
2018-02-06 19:47:37 +03:00
nco_val_desired = 2LL * ( clock_rate - abs_nco_freq ) ;
2017-06-13 15:54:47 +03:00
if ( nco_freq < 0 )
nco_val_desired = - nco_val_desired ;
}
nco_reg = max2175_round_closest ( nco_val_desired < < 20 , clock_rate ) ;
if ( nco_freq < 0 )
nco_reg + = 0x200000 ;
/* Check CSM is not busy */
ret = max2175_poll_csm_ready ( ctx ) ;
if ( ret )
return ret ;
mxm_dbg ( ctx , " freq %d desired %lld reg %u \n " ,
nco_freq , nco_val_desired , nco_reg ) ;
/* Write the calculated values to the appropriate registers */
max2175_write_bits ( ctx , 7 , 4 , 0 , ( nco_reg > > 16 ) & 0x1f ) ;
max2175_write ( ctx , 8 , nco_reg > > 8 ) ;
max2175_write ( ctx , 9 , nco_reg ) ;
return ret ;
}
static int max2175_set_rf_freq_non_am_bands ( struct max2175 * ctx , u64 freq ,
u32 lo_pos )
{
s64 adj_freq , low_if_freq ;
int ret ;
mxm_dbg ( ctx , " rf_freq: non AM bands \n " ) ;
if ( MAX2175_IS_FM_MODE ( ctx ) )
low_if_freq = 128000 ;
else if ( MAX2175_IS_FMHD_MODE ( ctx ) )
low_if_freq = 228000 ;
else
return max2175_set_lo_freq ( ctx , freq ) ;
if ( MAX2175_IS_BAND_VHF ( ctx ) = = ( lo_pos = = MAX2175_LO_ABOVE_DESIRED ) )
adj_freq = freq + low_if_freq ;
else
adj_freq = freq - low_if_freq ;
ret = max2175_set_lo_freq ( ctx , adj_freq ) ;
if ( ret )
return ret ;
return max2175_set_nco_freq ( ctx , - low_if_freq ) ;
}
static int max2175_set_rf_freq ( struct max2175 * ctx , u64 freq , u32 lo_pos )
{
int ret ;
if ( MAX2175_IS_BAND_AM ( ctx ) )
ret = max2175_set_nco_freq ( ctx , freq ) ;
else
ret = max2175_set_rf_freq_non_am_bands ( ctx , freq , lo_pos ) ;
mxm_dbg ( ctx , " set_rf_freq: ret %d freq %llu \n " , ret , freq ) ;
return ret ;
}
static int max2175_tune_rf_freq ( struct max2175 * ctx , u64 freq , u32 hsls )
{
int ret ;
ret = max2175_set_rf_freq ( ctx , freq , hsls ) ;
if ( ret )
return ret ;
ret = max2175_csm_action ( ctx , MAX2175_BUFFER_PLUS_PRESET_TUNE ) ;
if ( ret )
return ret ;
mxm_dbg ( ctx , " tune_rf_freq: old %u new %llu \n " , ctx - > freq , freq ) ;
ctx - > freq = freq ;
return ret ;
}
static void max2175_set_hsls ( struct max2175 * ctx , u32 lo_pos )
{
mxm_dbg ( ctx , " set_hsls: lo_pos %u \n " , lo_pos ) ;
if ( ( lo_pos = = MAX2175_LO_BELOW_DESIRED ) = = MAX2175_IS_BAND_VHF ( ctx ) )
max2175_write_bit ( ctx , 5 , 4 , 1 ) ;
else
max2175_write_bit ( ctx , 5 , 4 , 0 ) ;
}
static void max2175_set_eu_rx_mode ( struct max2175 * ctx , u32 rx_mode )
{
switch ( rx_mode ) {
case MAX2175_EU_FM_1_2 :
max2175_load_fmeu_1p2 ( ctx ) ;
break ;
case MAX2175_DAB_1_2 :
max2175_load_dab_1p2 ( ctx ) ;
break ;
}
/* Master is the default setting */
if ( ! ctx - > master )
max2175_write_bit ( ctx , 30 , 7 , 1 ) ;
}
static void max2175_set_na_rx_mode ( struct max2175 * ctx , u32 rx_mode )
{
switch ( rx_mode ) {
case MAX2175_NA_FM_1_0 :
max2175_load_fmna_1p0 ( ctx ) ;
break ;
case MAX2175_NA_FM_2_0 :
max2175_load_fmna_2p0 ( ctx ) ;
break ;
}
/* Master is the default setting */
if ( ! ctx - > master )
max2175_write_bit ( ctx , 30 , 7 , 1 ) ;
ctx - > decim_ratio = 27 ;
/* Load the Channel Filter Coefficients into channel filter bank #2 */
max2175_set_filter_coeffs ( ctx , MAX2175_CH_MSEL , 0 , ch_coeff_fmna ) ;
max2175_set_filter_coeffs ( ctx , MAX2175_EQ_MSEL , 0 ,
eq_coeff_fmna1_ra02_m6db ) ;
}
static int max2175_set_rx_mode ( struct max2175 * ctx , u32 rx_mode )
{
mxm_dbg ( ctx , " set_rx_mode: %u am_hiz %u \n " , rx_mode , ctx - > am_hiz ) ;
if ( ctx - > xtal_freq = = MAX2175_EU_XTAL_FREQ )
max2175_set_eu_rx_mode ( ctx , rx_mode ) ;
else
max2175_set_na_rx_mode ( ctx , rx_mode ) ;
if ( ctx - > am_hiz ) {
mxm_dbg ( ctx , " setting AM HiZ related config \n " ) ;
max2175_write_bit ( ctx , 50 , 5 , 1 ) ;
max2175_write_bit ( ctx , 90 , 7 , 1 ) ;
max2175_write_bits ( ctx , 73 , 1 , 0 , 2 ) ;
max2175_write_bits ( ctx , 80 , 5 , 0 , 33 ) ;
}
/* Load BB filter trim values saved in ROM */
max2175_set_bbfilter ( ctx ) ;
/* Set HSLS */
max2175_set_hsls ( ctx , ctx - > hsls - > cur . val ) ;
/* Use i2s enable settings */
max2175_i2s_enable ( ctx , ctx - > i2s_en - > cur . val ) ;
ctx - > mode_resolved = true ;
return 0 ;
}
static int max2175_rx_mode_from_freq ( struct max2175 * ctx , u32 freq , u32 * mode )
{
unsigned int i ;
int band = max2175_band_from_freq ( freq ) ;
/* Pick the first match always */
for ( i = 0 ; i < = ctx - > rx_mode - > maximum ; i + + ) {
if ( ctx - > rx_modes [ i ] . band = = band ) {
* mode = i ;
mxm_dbg ( ctx , " rx_mode_from_freq: freq %u mode %d \n " ,
freq , * mode ) ;
return 0 ;
}
}
return - EINVAL ;
}
static bool max2175_freq_rx_mode_valid ( struct max2175 * ctx ,
u32 mode , u32 freq )
{
int band = max2175_band_from_freq ( freq ) ;
return ( ctx - > rx_modes [ mode ] . band = = band ) ;
}
static void max2175_load_adc_presets ( struct max2175 * ctx )
{
unsigned int i , j ;
for ( i = 0 ; i < ARRAY_SIZE ( adc_presets ) ; i + + )
for ( j = 0 ; j < ARRAY_SIZE ( adc_presets [ 0 ] ) ; j + + )
max2175_write ( ctx , 146 + j + i * 55 , adc_presets [ i ] [ j ] ) ;
}
static int max2175_init_power_manager ( struct max2175 * ctx )
{
int ret ;
/* Execute on-chip power-up/calibration */
max2175_write_bit ( ctx , 99 , 2 , 0 ) ;
usleep_range ( 1000 , 1500 ) ;
max2175_write_bit ( ctx , 99 , 2 , 1 ) ;
/* Wait for the power manager to finish. */
ret = max2175_poll_timeout ( ctx , 69 , 7 , 7 , 1 , 50000 ) ;
if ( ret )
mxm_err ( ctx , " init pm failed \n " ) ;
return ret ;
}
static int max2175_recalibrate_adc ( struct max2175 * ctx )
{
int ret ;
/* ADC Re-calibration */
max2175_write ( ctx , 150 , 0xff ) ;
max2175_write ( ctx , 205 , 0xff ) ;
max2175_write ( ctx , 147 , 0x20 ) ;
max2175_write ( ctx , 147 , 0x00 ) ;
max2175_write ( ctx , 202 , 0x20 ) ;
max2175_write ( ctx , 202 , 0x00 ) ;
ret = max2175_poll_timeout ( ctx , 69 , 4 , 3 , 3 , 50000 ) ;
if ( ret )
mxm_err ( ctx , " adc recalibration failed \n " ) ;
return ret ;
}
static u8 max2175_read_rom ( struct max2175 * ctx , u8 row )
{
u8 data = 0 ;
max2175_write_bit ( ctx , 56 , 4 , 0 ) ;
max2175_write_bits ( ctx , 56 , 3 , 0 , row ) ;
usleep_range ( 2000 , 2500 ) ;
max2175_read ( ctx , 58 , & data ) ;
max2175_write_bits ( ctx , 56 , 3 , 0 , 0 ) ;
mxm_dbg ( ctx , " read_rom: row %d data 0x%02x \n " , row , data ) ;
return data ;
}
static void max2175_load_from_rom ( struct max2175 * ctx )
{
u8 data = 0 ;
data = max2175_read_rom ( ctx , 0 ) ;
ctx - > rom_bbf_bw_am = data & 0x0f ;
max2175_write_bits ( ctx , 81 , 3 , 0 , data > > 4 ) ;
data = max2175_read_rom ( ctx , 1 ) ;
ctx - > rom_bbf_bw_fm = data & 0x0f ;
ctx - > rom_bbf_bw_dab = data > > 4 ;
data = max2175_read_rom ( ctx , 2 ) ;
max2175_write_bits ( ctx , 82 , 4 , 0 , data & 0x1f ) ;
max2175_write_bits ( ctx , 82 , 7 , 5 , data > > 5 ) ;
data = max2175_read_rom ( ctx , 3 ) ;
if ( ctx - > am_hiz ) {
data & = 0x0f ;
data | = ( max2175_read_rom ( ctx , 7 ) & 0x40 ) > > 2 ;
if ( ! data )
data | = 2 ;
} else {
data = ( data & 0xf0 ) > > 4 ;
data | = ( max2175_read_rom ( ctx , 7 ) & 0x80 ) > > 3 ;
if ( ! data )
data | = 30 ;
}
max2175_write_bits ( ctx , 80 , 5 , 0 , data + 31 ) ;
data = max2175_read_rom ( ctx , 6 ) ;
max2175_write_bits ( ctx , 81 , 7 , 6 , data > > 6 ) ;
}
static void max2175_load_full_fm_eu_1p0 ( struct max2175 * ctx )
{
unsigned int i ;
for ( i = 0 ; i < ARRAY_SIZE ( full_fm_eu_1p0 ) ; i + + )
max2175_write ( ctx , i + 1 , full_fm_eu_1p0 [ i ] ) ;
usleep_range ( 5000 , 5500 ) ;
ctx - > decim_ratio = 36 ;
}
static void max2175_load_full_fm_na_1p0 ( struct max2175 * ctx )
{
unsigned int i ;
for ( i = 0 ; i < ARRAY_SIZE ( full_fm_na_1p0 ) ; i + + )
max2175_write ( ctx , i + 1 , full_fm_na_1p0 [ i ] ) ;
usleep_range ( 5000 , 5500 ) ;
ctx - > decim_ratio = 27 ;
}
static int max2175_core_init ( struct max2175 * ctx , u32 refout_bits )
{
int ret ;
/* MAX2175 uses 36.864MHz clock for EU & 40.154MHz for NA region */
if ( ctx - > xtal_freq = = MAX2175_EU_XTAL_FREQ )
max2175_load_full_fm_eu_1p0 ( ctx ) ;
else
max2175_load_full_fm_na_1p0 ( ctx ) ;
/* The default settings assume master */
if ( ! ctx - > master )
max2175_write_bit ( ctx , 30 , 7 , 1 ) ;
mxm_dbg ( ctx , " refout_bits %u \n " , refout_bits ) ;
/* Set REFOUT */
max2175_write_bits ( ctx , 56 , 7 , 5 , refout_bits ) ;
/* ADC Reset */
max2175_write_bit ( ctx , 99 , 1 , 0 ) ;
usleep_range ( 1000 , 1500 ) ;
max2175_write_bit ( ctx , 99 , 1 , 1 ) ;
/* Load ADC preset values */
max2175_load_adc_presets ( ctx ) ;
/* Initialize the power management state machine */
ret = max2175_init_power_manager ( ctx ) ;
if ( ret )
return ret ;
/* Recalibrate ADC */
ret = max2175_recalibrate_adc ( ctx ) ;
if ( ret )
return ret ;
/* Load ROM values to appropriate registers */
max2175_load_from_rom ( ctx ) ;
if ( ctx - > xtal_freq = = MAX2175_EU_XTAL_FREQ ) {
/* Load FIR coefficients into bank 0 */
max2175_set_filter_coeffs ( ctx , MAX2175_CH_MSEL , 0 ,
ch_coeff_fmeu ) ;
max2175_set_filter_coeffs ( ctx , MAX2175_EQ_MSEL , 0 ,
eq_coeff_fmeu1_ra02_m6db ) ;
} else {
/* Load FIR coefficients into bank 0 */
max2175_set_filter_coeffs ( ctx , MAX2175_CH_MSEL , 0 ,
ch_coeff_fmna ) ;
max2175_set_filter_coeffs ( ctx , MAX2175_EQ_MSEL , 0 ,
eq_coeff_fmna1_ra02_m6db ) ;
}
mxm_dbg ( ctx , " core initialized \n " ) ;
return 0 ;
}
static void max2175_s_ctrl_rx_mode ( struct max2175 * ctx , u32 rx_mode )
{
/* Load mode. Range check already done */
max2175_set_rx_mode ( ctx , rx_mode ) ;
mxm_dbg ( ctx , " s_ctrl_rx_mode: %u curr freq %u \n " , rx_mode , ctx - > freq ) ;
/* Check if current freq valid for mode & update */
if ( max2175_freq_rx_mode_valid ( ctx , rx_mode , ctx - > freq ) )
max2175_tune_rf_freq ( ctx , ctx - > freq , ctx - > hsls - > cur . val ) ;
else
/* Use default freq of mode if current freq is not valid */
max2175_tune_rf_freq ( ctx , ctx - > rx_modes [ rx_mode ] . freq ,
ctx - > hsls - > cur . val ) ;
}
static int max2175_s_ctrl ( struct v4l2_ctrl * ctrl )
{
struct max2175 * ctx = max2175_from_ctrl_hdl ( ctrl - > handler ) ;
mxm_dbg ( ctx , " s_ctrl: id 0x%x, val %u \n " , ctrl - > id , ctrl - > val ) ;
switch ( ctrl - > id ) {
case V4L2_CID_MAX2175_I2S_ENABLE :
max2175_i2s_enable ( ctx , ctrl - > val ) ;
break ;
case V4L2_CID_MAX2175_HSLS :
max2175_set_hsls ( ctx , ctrl - > val ) ;
break ;
case V4L2_CID_MAX2175_RX_MODE :
max2175_s_ctrl_rx_mode ( ctx , ctrl - > val ) ;
break ;
}
return 0 ;
}
static u32 max2175_get_lna_gain ( struct max2175 * ctx )
{
enum max2175_band band = max2175_read_bits ( ctx , 5 , 1 , 0 ) ;
switch ( band ) {
case MAX2175_BAND_AM :
return max2175_read_bits ( ctx , 51 , 3 , 0 ) ;
case MAX2175_BAND_FM :
return max2175_read_bits ( ctx , 50 , 3 , 0 ) ;
case MAX2175_BAND_VHF :
return max2175_read_bits ( ctx , 52 , 5 , 0 ) ;
default :
return 0 ;
}
}
static int max2175_g_volatile_ctrl ( struct v4l2_ctrl * ctrl )
{
struct max2175 * ctx = max2175_from_ctrl_hdl ( ctrl - > handler ) ;
switch ( ctrl - > id ) {
case V4L2_CID_RF_TUNER_LNA_GAIN :
ctrl - > val = max2175_get_lna_gain ( ctx ) ;
break ;
case V4L2_CID_RF_TUNER_IF_GAIN :
ctrl - > val = max2175_read_bits ( ctx , 49 , 4 , 0 ) ;
break ;
case V4L2_CID_RF_TUNER_PLL_LOCK :
ctrl - > val = ( max2175_read_bits ( ctx , 60 , 7 , 6 ) = = 3 ) ;
break ;
}
return 0 ;
} ;
static int max2175_set_freq_and_mode ( struct max2175 * ctx , u32 freq )
{
u32 rx_mode ;
int ret ;
/* Get band from frequency */
ret = max2175_rx_mode_from_freq ( ctx , freq , & rx_mode ) ;
if ( ret )
return ret ;
mxm_dbg ( ctx , " set_freq_and_mode: freq %u rx_mode %d \n " , freq , rx_mode ) ;
/* Load mode */
max2175_set_rx_mode ( ctx , rx_mode ) ;
ctx - > rx_mode - > cur . val = rx_mode ;
/* Tune to the new freq given */
return max2175_tune_rf_freq ( ctx , freq , ctx - > hsls - > cur . val ) ;
}
static int max2175_s_frequency ( struct v4l2_subdev * sd ,
const struct v4l2_frequency * vf )
{
struct max2175 * ctx = max2175_from_sd ( sd ) ;
u32 freq ;
int ret = 0 ;
mxm_dbg ( ctx , " s_freq: new %u curr %u, mode_resolved %d \n " ,
vf - > frequency , ctx - > freq , ctx - > mode_resolved ) ;
if ( vf - > tuner ! = 0 )
return - EINVAL ;
freq = clamp ( vf - > frequency , ctx - > bands_rf - > rangelow ,
ctx - > bands_rf - > rangehigh ) ;
/* Check new freq valid for rx_mode if already resolved */
if ( ctx - > mode_resolved & &
max2175_freq_rx_mode_valid ( ctx , ctx - > rx_mode - > cur . val , freq ) )
ret = max2175_tune_rf_freq ( ctx , freq , ctx - > hsls - > cur . val ) ;
else
/* Find default rx_mode for freq and tune to it */
ret = max2175_set_freq_and_mode ( ctx , freq ) ;
mxm_dbg ( ctx , " s_freq: ret %d curr %u mode_resolved %d mode %u \n " ,
ret , ctx - > freq , ctx - > mode_resolved , ctx - > rx_mode - > cur . val ) ;
return ret ;
}
static int max2175_g_frequency ( struct v4l2_subdev * sd ,
struct v4l2_frequency * vf )
{
struct max2175 * ctx = max2175_from_sd ( sd ) ;
if ( vf - > tuner ! = 0 )
return - EINVAL ;
/* RF freq */
vf - > type = V4L2_TUNER_RF ;
vf - > frequency = ctx - > freq ;
2021-02-22 16:13:17 +03:00
return 0 ;
2017-06-13 15:54:47 +03:00
}
static int max2175_enum_freq_bands ( struct v4l2_subdev * sd ,
struct v4l2_frequency_band * band )
{
struct max2175 * ctx = max2175_from_sd ( sd ) ;
if ( band - > tuner ! = 0 | | band - > index ! = 0 )
return - EINVAL ;
* band = * ctx - > bands_rf ;
return 0 ;
}
static int max2175_g_tuner ( struct v4l2_subdev * sd , struct v4l2_tuner * vt )
{
struct max2175 * ctx = max2175_from_sd ( sd ) ;
if ( vt - > index > 0 )
return - EINVAL ;
2018-09-10 15:19:14 +03:00
strscpy ( vt - > name , " RF " , sizeof ( vt - > name ) ) ;
2017-06-13 15:54:47 +03:00
vt - > type = V4L2_TUNER_RF ;
vt - > capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS ;
vt - > rangelow = ctx - > bands_rf - > rangelow ;
vt - > rangehigh = ctx - > bands_rf - > rangehigh ;
return 0 ;
}
static int max2175_s_tuner ( struct v4l2_subdev * sd , const struct v4l2_tuner * vt )
{
/* Check tuner index is valid */
if ( vt - > index > 0 )
return - EINVAL ;
return 0 ;
}
static const struct v4l2_subdev_tuner_ops max2175_tuner_ops = {
. s_frequency = max2175_s_frequency ,
. g_frequency = max2175_g_frequency ,
. enum_freq_bands = max2175_enum_freq_bands ,
. g_tuner = max2175_g_tuner ,
. s_tuner = max2175_s_tuner ,
} ;
static const struct v4l2_subdev_ops max2175_ops = {
. tuner = & max2175_tuner_ops ,
} ;
static const struct v4l2_ctrl_ops max2175_ctrl_ops = {
. s_ctrl = max2175_s_ctrl ,
. g_volatile_ctrl = max2175_g_volatile_ctrl ,
} ;
/*
* I2S output enable / disable configuration . This is a private control .
2020-03-06 10:50:46 +03:00
* Refer to Documentation / userspace - api / media / drivers / max2175 . rst for more details .
2017-06-13 15:54:47 +03:00
*/
static const struct v4l2_ctrl_config max2175_i2s_en = {
. ops = & max2175_ctrl_ops ,
. id = V4L2_CID_MAX2175_I2S_ENABLE ,
. name = " I2S Enable " ,
. type = V4L2_CTRL_TYPE_BOOLEAN ,
. min = 0 ,
. max = 1 ,
. step = 1 ,
. def = 1 ,
. is_private = 1 ,
} ;
/*
* HSLS value control LO freq adjacent location configuration .
2020-03-06 10:50:46 +03:00
* Refer to Documentation / userspace - api / media / drivers / max2175 . rst for more details .
2017-06-13 15:54:47 +03:00
*/
static const struct v4l2_ctrl_config max2175_hsls = {
. ops = & max2175_ctrl_ops ,
. id = V4L2_CID_MAX2175_HSLS ,
. name = " HSLS Above/Below Desired " ,
. type = V4L2_CTRL_TYPE_BOOLEAN ,
. min = 0 ,
. max = 1 ,
. step = 1 ,
. def = 1 ,
} ;
/*
* Rx modes below are a set of preset configurations that decides the tuner ' s
* sck and sample rate of transmission . They are separate for EU & NA regions .
2020-03-06 10:50:46 +03:00
* Refer to Documentation / userspace - api / media / drivers / max2175 . rst for more details .
2017-06-13 15:54:47 +03:00
*/
static const char * const max2175_ctrl_eu_rx_modes [ ] = {
[ MAX2175_EU_FM_1_2 ] = " EU FM 1.2 " ,
[ MAX2175_DAB_1_2 ] = " DAB 1.2 " ,
} ;
static const char * const max2175_ctrl_na_rx_modes [ ] = {
[ MAX2175_NA_FM_1_0 ] = " NA FM 1.0 " ,
[ MAX2175_NA_FM_2_0 ] = " NA FM 2.0 " ,
} ;
static const struct v4l2_ctrl_config max2175_eu_rx_mode = {
. ops = & max2175_ctrl_ops ,
. id = V4L2_CID_MAX2175_RX_MODE ,
. name = " RX Mode " ,
. type = V4L2_CTRL_TYPE_MENU ,
. max = ARRAY_SIZE ( max2175_ctrl_eu_rx_modes ) - 1 ,
. def = 0 ,
. qmenu = max2175_ctrl_eu_rx_modes ,
} ;
static const struct v4l2_ctrl_config max2175_na_rx_mode = {
. ops = & max2175_ctrl_ops ,
. id = V4L2_CID_MAX2175_RX_MODE ,
. name = " RX Mode " ,
. type = V4L2_CTRL_TYPE_MENU ,
. max = ARRAY_SIZE ( max2175_ctrl_na_rx_modes ) - 1 ,
. def = 0 ,
. qmenu = max2175_ctrl_na_rx_modes ,
} ;
static int max2175_refout_load_to_bits ( struct i2c_client * client , u32 load ,
u32 * bits )
{
2017-06-20 13:11:15 +03:00
if ( load < = 40 )
2017-06-13 15:54:47 +03:00
* bits = load / 10 ;
else if ( load > = 60 & & load < = 70 )
* bits = load / 10 - 1 ;
else
return - EINVAL ;
return 0 ;
}
2019-07-11 00:51:49 +03:00
static int max2175_probe ( struct i2c_client * client )
2017-06-13 15:54:47 +03:00
{
bool master = true , am_hiz = false ;
u32 refout_load , refout_bits = 0 ; /* REFOUT disabled */
struct v4l2_ctrl_handler * hdl ;
struct fwnode_handle * fwnode ;
struct device_node * np ;
struct v4l2_subdev * sd ;
struct regmap * regmap ;
struct max2175 * ctx ;
struct clk * clk ;
int ret ;
/* Parse DT properties */
np = of_parse_phandle ( client - > dev . of_node , " maxim,master " , 0 ) ;
if ( np ) {
master = false ; /* Slave tuner */
of_node_put ( np ) ;
}
fwnode = of_fwnode_handle ( client - > dev . of_node ) ;
if ( fwnode_property_present ( fwnode , " maxim,am-hiz-filter " ) )
am_hiz = true ;
if ( ! fwnode_property_read_u32 ( fwnode , " maxim,refout-load " ,
& refout_load ) ) {
ret = max2175_refout_load_to_bits ( client , refout_load ,
& refout_bits ) ;
if ( ret ) {
dev_err ( & client - > dev , " invalid refout_load %u \n " ,
refout_load ) ;
return - EINVAL ;
}
}
clk = devm_clk_get ( & client - > dev , NULL ) ;
if ( IS_ERR ( clk ) ) {
ret = PTR_ERR ( clk ) ;
dev_err ( & client - > dev , " cannot get clock %d \n " , ret ) ;
2017-08-27 19:30:35 +03:00
return ret ;
2017-06-13 15:54:47 +03:00
}
regmap = devm_regmap_init_i2c ( client , & max2175_regmap_config ) ;
if ( IS_ERR ( regmap ) ) {
ret = PTR_ERR ( regmap ) ;
dev_err ( & client - > dev , " regmap init failed %d \n " , ret ) ;
return - ENODEV ;
}
/* Alloc tuner context */
ctx = devm_kzalloc ( & client - > dev , sizeof ( * ctx ) , GFP_KERNEL ) ;
if ( ctx = = NULL )
return - ENOMEM ;
sd = & ctx - > sd ;
ctx - > master = master ;
ctx - > am_hiz = am_hiz ;
ctx - > mode_resolved = false ;
ctx - > regmap = regmap ;
ctx - > xtal_freq = clk_get_rate ( clk ) ;
dev_info ( & client - > dev , " xtal freq %luHz \n " , ctx - > xtal_freq ) ;
v4l2_i2c_subdev_init ( sd , client , & max2175_ops ) ;
ctx - > client = client ;
2017-10-19 19:31:21 +03:00
sd - > flags | = V4L2_SUBDEV_FL_HAS_DEVNODE ;
2017-06-13 15:54:47 +03:00
/* Controls */
hdl = & ctx - > ctrl_hdl ;
ret = v4l2_ctrl_handler_init ( hdl , 7 ) ;
if ( ret )
return ret ;
ctx - > lna_gain = v4l2_ctrl_new_std ( hdl , & max2175_ctrl_ops ,
V4L2_CID_RF_TUNER_LNA_GAIN ,
0 , 63 , 1 , 0 ) ;
ctx - > lna_gain - > flags | = ( V4L2_CTRL_FLAG_VOLATILE |
V4L2_CTRL_FLAG_READ_ONLY ) ;
ctx - > if_gain = v4l2_ctrl_new_std ( hdl , & max2175_ctrl_ops ,
V4L2_CID_RF_TUNER_IF_GAIN ,
0 , 31 , 1 , 0 ) ;
ctx - > if_gain - > flags | = ( V4L2_CTRL_FLAG_VOLATILE |
V4L2_CTRL_FLAG_READ_ONLY ) ;
ctx - > pll_lock = v4l2_ctrl_new_std ( hdl , & max2175_ctrl_ops ,
V4L2_CID_RF_TUNER_PLL_LOCK ,
0 , 1 , 1 , 0 ) ;
ctx - > pll_lock - > flags | = ( V4L2_CTRL_FLAG_VOLATILE |
V4L2_CTRL_FLAG_READ_ONLY ) ;
ctx - > i2s_en = v4l2_ctrl_new_custom ( hdl , & max2175_i2s_en , NULL ) ;
ctx - > hsls = v4l2_ctrl_new_custom ( hdl , & max2175_hsls , NULL ) ;
if ( ctx - > xtal_freq = = MAX2175_EU_XTAL_FREQ ) {
ctx - > rx_mode = v4l2_ctrl_new_custom ( hdl ,
& max2175_eu_rx_mode , NULL ) ;
ctx - > rx_modes = eu_rx_modes ;
ctx - > bands_rf = & eu_bands_rf ;
} else {
ctx - > rx_mode = v4l2_ctrl_new_custom ( hdl ,
& max2175_na_rx_mode , NULL ) ;
ctx - > rx_modes = na_rx_modes ;
ctx - > bands_rf = & na_bands_rf ;
}
ctx - > sd . ctrl_handler = & ctx - > ctrl_hdl ;
/* Set the defaults */
ctx - > freq = ctx - > bands_rf - > rangelow ;
/* Register subdev */
ret = v4l2_async_register_subdev ( sd ) ;
if ( ret ) {
dev_err ( & client - > dev , " register subdev failed \n " ) ;
goto err_reg ;
}
/* Initialize device */
ret = max2175_core_init ( ctx , refout_bits ) ;
if ( ret )
goto err_init ;
ret = v4l2_ctrl_handler_setup ( hdl ) ;
if ( ret )
goto err_init ;
return 0 ;
err_init :
v4l2_async_unregister_subdev ( sd ) ;
err_reg :
v4l2_ctrl_handler_free ( & ctx - > ctrl_hdl ) ;
return ret ;
}
2022-08-15 11:02:30 +03:00
static void max2175_remove ( struct i2c_client * client )
2017-06-13 15:54:47 +03:00
{
struct v4l2_subdev * sd = i2c_get_clientdata ( client ) ;
struct max2175 * ctx = max2175_from_sd ( sd ) ;
v4l2_ctrl_handler_free ( & ctx - > ctrl_hdl ) ;
v4l2_async_unregister_subdev ( sd ) ;
}
static const struct i2c_device_id max2175_id [ ] = {
{ DRIVER_NAME , 0 } ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( i2c , max2175_id ) ;
static const struct of_device_id max2175_of_ids [ ] = {
{ . compatible = " maxim,max2175 " , } ,
{ }
} ;
MODULE_DEVICE_TABLE ( of , max2175_of_ids ) ;
static struct i2c_driver max2175_driver = {
. driver = {
. name = DRIVER_NAME ,
. of_match_table = max2175_of_ids ,
} ,
2019-07-11 00:51:49 +03:00
. probe_new = max2175_probe ,
2017-06-13 15:54:47 +03:00
. remove = max2175_remove ,
. id_table = max2175_id ,
} ;
module_i2c_driver ( max2175_driver ) ;
MODULE_DESCRIPTION ( " Maxim MAX2175 RF to Bits tuner driver " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;
MODULE_AUTHOR ( " Ramesh Shanmugasundaram <ramesh.shanmugasundaram@bp.renesas.com> " ) ;