Hi Ramesh, Thank you for the patch. On Wednesday 12 Oct 2016 15:10:25 Ramesh Shanmugasundaram wrote: > This patch adds driver support for MAX2175 chip. This is Maxim > Integrated's RF to Bits tuner front end chip designed for software-defined > radio solutions. This driver exposes the tuner as a sub-device instance > with standard and custom controls to configure the device. > > Signed-off-by: Ramesh Shanmugasundaram > <ramesh.shanmugasundaram@xxxxxxxxxxxxxx> --- > .../devicetree/bindings/media/i2c/max2175.txt | 60 + > drivers/media/i2c/Kconfig | 4 + > drivers/media/i2c/Makefile | 2 + > drivers/media/i2c/max2175/Kconfig | 8 + > drivers/media/i2c/max2175/Makefile | 4 + > drivers/media/i2c/max2175/max2175.c | 1624 +++++++++++++++++ > drivers/media/i2c/max2175/max2175.h | 124 ++ > 7 files changed, 1826 insertions(+) > create mode 100644 Documentation/devicetree/bindings/media/i2c/max2175.txt > create mode 100644 drivers/media/i2c/max2175/Kconfig > create mode 100644 drivers/media/i2c/max2175/Makefile > create mode 100644 drivers/media/i2c/max2175/max2175.c > create mode 100644 drivers/media/i2c/max2175/max2175.h > > diff --git a/Documentation/devicetree/bindings/media/i2c/max2175.txt > b/Documentation/devicetree/bindings/media/i2c/max2175.txt new file mode > 100644 > index 0000000..2250d5f > --- /dev/null > +++ b/Documentation/devicetree/bindings/media/i2c/max2175.txt > @@ -0,0 +1,60 @@ > +Maxim Integrated MAX2175 RF to Bits tuner > +----------------------------------------- > + > +The MAX2175 IC is an advanced analog/digital hybrid-radio receiver with > +RF to Bits® front-end designed for software-defined radio solutions. > + > +Required properties: > +-------------------- > +- compatible: "maxim,max2175" for MAX2175 RF-to-bits tuner. > +- clocks: phandle to the fixed xtal clock. > +- clock-names: name of the fixed xtal clock. > +- port: video interface child port node of a tuner that defines the local As Rob pointed out in his review of patch 3/5, this isn't video. > + and remote endpoints. The remote endpoint is assumed to be an SDR device > + that is capable of receiving the digital samples from the tuner. > + > +Optional properties: > +-------------------- > +- maxim,slave : empty property indicates this is a slave of another > + master tuner. This is used to define two tuners in > + diversity mode (1 master, 1 slave). By default each > + tuner is an individual master. Would it be useful to make that property a phandle to the master tuner, to give drivers a way to access the master ? I haven't checked whether there could be use cases for that. > +- maxim,refout-load: load capacitance value (in pF) on reference output > + drive level. The mapping of these load values to > + respective bit values are given below. > + 0 - Reference output disabled > + 1 - 10pF load > + 2 - 20pF load > + 3 - 30pF load > + 4 - 40pF load > + 5 - 60pF load > + 6 - 70pF load As Geert pointed out, you can simply specify the value in pF. > + > +Example: > +-------- > + > +Board specific DTS file > + > +/* Fixed XTAL clock node */ > +maxim_xtal: maximextal { > + compatible = "fixed-clock"; > + #clock-cells = <0>; > + clock-frequency = <36864000>; > +}; > + > +/* A tuner device instance under i2c bus */ > +max2175_0: tuner@60 { > + #clock-cells = <0>; Is the tuner a clock provider ? If it isn't you don't need this property. > + compatible = "maxim,max2175"; > + reg = <0x60>; > + clocks = <&maxim_xtal>; > + clock-names = "xtal"; > + maxim,refout-load = <10>; > + > + port { > + max2175_0_ep: endpoint { > + remote-endpoint = <&slave_rx_v4l2_sdr_device>; > + }; > + }; > + > +}; [snip] > diff --git a/drivers/media/i2c/max2175/Makefile > b/drivers/media/i2c/max2175/Makefile new file mode 100644 > index 0000000..9bb46ac > --- /dev/null > +++ b/drivers/media/i2c/max2175/Makefile > @@ -0,0 +1,4 @@ > +# > +# Makefile for Maxim RF to Bits tuner device > +# > +obj-$(CONFIG_SDR_MAX2175) += max2175.o If there's a single source file you might want to move it to drivers/media/i2c/. > diff --git a/drivers/media/i2c/max2175/max2175.c > b/drivers/media/i2c/max2175/max2175.c new file mode 100644 > index 0000000..71b60c2 > --- /dev/null > +++ b/drivers/media/i2c/max2175/max2175.c > @@ -0,0 +1,1624 @@ > +/* > + * 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 chip. > + * > + * Copyright (C) 2016 Maxim Integrated Products > + * Copyright (C) 2016 Renesas Electronics Corporation > + * > + * 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. > + */ > + > +#include <linux/clk.h> > +#include <linux/errno.h> > +#include <linux/i2c.h> > +#include <linux/init.h> > +#include <linux/interrupt.h> > +#include <linux/kernel.h> > +#include <linux/module.h> > +#include <linux/slab.h> > +#include <linux/delay.h> You can move delay.h right below clk.h and everything will be in alphabetical order :-) > +#include <media/v4l2-ctrls.h> > +#include <media/v4l2-device.h> > +#include <media/v4l2-of.h> > + > +#include "max2175.h" > + > +#define DRIVER_NAME "max2175" > + > +static unsigned int max2175_debug; > +module_param(max2175_debug, uint, 0644); > +MODULE_PARM_DESC(max2175_debug, "activate debug info"); You can name the parameter "debug". > +#define mxm_dbg(ctx, fmt, arg...) \ > + v4l2_dbg(1, max2175_debug, &ctx->sd, fmt, ## arg) [snip] > +/* Preset values: > + * Based on Maxim MAX2175 Register Table revision: 130p10 > + */ The preferred multi-line comment style is /* * foo * bar */ [snip] > +struct max2175_ctx { Nitpicking, such a structure would usually be named max2175 or max2175_device. Context seems to imply that you can have multiple of them per device. > + struct v4l2_subdev sd; > + struct i2c_client *client; > + struct device *dev; > + > + /* Cached configuration */ > + u8 regs[256]; If you want to cache register values you should use regmap. > + enum max2175_modetag mode; /* Receive mode tag */ > + u32 freq; /* In Hz */ > + struct max2175_rxmode *rx_modes; > + > + /* Device settings */ > + bool master; > + u32 decim_ratio; > + u64 xtal_freq; > + > + /* ROM values */ > + u8 rom_bbf_bw_am; > + u8 rom_bbf_bw_fm; > + u8 rom_bbf_bw_dab; > + > + /* Local copy of old settings */ > + u8 i2s_test; > + > + u8 nbd_gain; > + u8 nbd_threshold; > + u8 wbd_gain; > + u8 wbd_threshold; > + u8 bbd_threshold; > + u8 bbdclip_threshold; > + u8 lt_wbd_threshold; > + u8 lt_wbd_gain; > + > + /* 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 *i2s_mode; /* I2S mode value */ > + struct v4l2_ctrl *am_hiz; /* AM High impledance input */ > + struct v4l2_ctrl *hsls; /* High-side/Low-side polarity */ > + struct v4l2_ctrl *rx_mode; /* Receive mode */ > + > + /* Driver private variables */ > + bool mode_resolved; /* Flag to sanity check settings */ > +}; [snip] > +/* Local store bitops helpers */ > +static u8 max2175_get_bits(struct max2175_ctx *ctx, u8 idx, u8 msb, u8 lsb) > +{ > + if (max2175_debug >= 2) > + mxm_dbg(ctx, "get_bits: idx:%u msb:%u lsb:%u\n", > + idx, msb, lsb); Do we really need such detailed debugging ? > + return __max2175_get_bits(ctx->regs[idx], msb, lsb); > +} > + > +static bool max2175_get_bit(struct max2175_ctx *ctx, u8 idx, u8 bit) > +{ > + return !!max2175_get_bits(ctx, idx, bit, bit); > +} > + > +static void max2175_set_bits(struct max2175_ctx *ctx, u8 idx, > + u8 msb, u8 lsb, u8 newval) > +{ > + if (max2175_debug >= 2) > + mxm_dbg(ctx, "set_bits: idx:%u msb:%u lsb:%u newval:%u\n", > + idx, msb, lsb, newval); > + ctx->regs[idx] = __max2175_set_bits(ctx->regs[idx], msb, lsb, > + newval); > +} > + > +static void max2175_set_bit(struct max2175_ctx *ctx, u8 idx, u8 bit, u8 > newval) > +{ > + max2175_set_bits(ctx, idx, bit, bit, newval); > +} > + > +/* Device register bitops helpers */ > +static u8 max2175_read_bits(struct max2175_ctx *ctx, u8 idx, u8 msb, u8 > lsb) > +{ > + return __max2175_get_bits(max2175_reg_read(ctx, idx), msb, lsb); > +} > + > +static void max2175_write_bits(struct max2175_ctx *ctx, u8 idx, u8 msb, > + u8 lsb, u8 newval) > +{ > + /* Update local copy & write to device */ > + max2175_set_bits(ctx, idx, msb, lsb, newval); > + max2175_reg_write(ctx, idx, ctx->regs[idx]); > +} > + > +static void max2175_write_bit(struct max2175_ctx *ctx, u8 idx, u8 bit, > + u8 newval) > +{ > + if (max2175_debug >= 2) > + mxm_dbg(ctx, "idx %u, bit %u, newval %u\n", idx, bit, newval); > + max2175_write_bits(ctx, idx, bit, bit, newval); > +} Really, please use regmap :-) > +static int max2175_poll_timeout(struct max2175_ctx *ctx, u8 idx, u8 msb, u8 > lsb, > + u8 exp_val, u32 timeout) > +{ > + unsigned long end = jiffies + msecs_to_jiffies(timeout); > + int ret; > + > + do { > + ret = max2175_read_bits(ctx, idx, msb, lsb); > + if (ret < 0) > + return ret; > + if (ret == exp_val) > + return 0; > + > + usleep_range(1000, 1500); > + } while (time_is_after_jiffies(end)); > + > + return -EBUSY; > +} > + > +static int max2175_poll_csm_ready(struct max2175_ctx *ctx) > +{ > + return max2175_poll_timeout(ctx, 69, 1, 1, 0, 50); Please define macros for register addresses and values, this is just unreadable. > +} [snip] > + > +#define MAX2175_IS_BAND_AM(ctx) \ > + (max2175_get_bits(ctx, 5, 1, 0) == MAX2175_BAND_AM) > + > +#define MAX2175_IS_FM_MODE(ctx) \ > + (max2175_get_bits(ctx, 12, 5, 4) == 0) > + > +#define MAX2175_IS_FMHD_MODE(ctx) \ > + (max2175_get_bits(ctx, 12, 5, 4) == 1) > + > +#define MAX2175_IS_DAB_MODE(ctx) \ > + (max2175_get_bits(ctx, 12, 5, 4) == 2) > + > +static int max2175_band_from_freq(u64 freq) > +{ > + if (freq >= 144000 && freq <= 26100000) > + return MAX2175_BAND_AM; > + else if (freq >= 65000000 && freq <= 108000000) > + return MAX2175_BAND_FM; > + else if (freq >= 160000000 && freq <= 240000000) > + return MAX2175_BAND_VHF; > + > + /* Add other bands on need basis */ > + return -ENOTSUPP; > +} > + > +static int max2175_update_i2s_mode(struct max2175_ctx *ctx, u32 i2s_mode) > +{ > + /* Only change if it's new */ > + if (max2175_read_bits(ctx, 29, 2, 0) == i2s_mode) > + return 0; > + > + max2175_write_bits(ctx, 29, 2, 0, i2s_mode); > + > + /* Based on I2S mode value I2S_WORD_CNT values change */ > + if (i2s_mode == MAX2175_I2S_MODE3) { > + max2175_write_bits(ctx, 30, 6, 0, 1); > + } else if (i2s_mode == MAX2175_I2S_MODE2 || > + i2s_mode == MAX2175_I2S_MODE4) { > + max2175_write_bits(ctx, 30, 6, 0, 0); > + } else if (i2s_mode == MAX2175_I2S_MODE0) { > + max2175_write_bits(ctx, 30, 6, 0, > + ctx->rx_modes[ctx->mode].i2s_word_size); > + } else { > + v4l2_err(ctx->client, > + "failed: i2s_mode %u unsupported\n", i2s_mode); This should never happen as the control framework will validate control values. > + return 1; Error codes should be negative. > + } > + mxm_dbg(ctx, "updated i2s_mode %u\n", i2s_mode); > + return 0; > +} [snip] > +static void max2175_load_dab_1p2(struct max2175_ctx *ctx) > +{ > + u32 i; unsigned int will do, no need for an explicitly sized type. > + > + /* Master is already set on init */ > + for (i = 0; i < ARRAY_SIZE(dab12_map); i++) > + max2175_reg_write(ctx, dab12_map[i].idx, dab12_map[i].val); > + > + /* The default settings assume master */ > + if (!ctx->master) > + max2175_write_bit(ctx, 30, 7, 1); > + > + /* Cache i2s_test value at this point */ > + ctx->i2s_test = max2175_get_bits(ctx, 104, 3, 0); > + 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); > +} [snip] > +static bool max2175_set_csm_mode(struct max2175_ctx *ctx, > + enum max2175_csm_mode new_mode) > +{ > + int ret = max2175_poll_csm_ready(ctx); > + > + if (ret) { > + v4l2_err(ctx->client, "csm not ready: new mode %d\n", new_mode); > + return ret; > + } > + > + mxm_dbg(ctx, "set csm mode: new mode %d\n", new_mode); > + > + max2175_write_bits(ctx, 0, 2, 0, new_mode); > + > + /* Wait for a fixed settle down time depending on new mode and band */ > + switch (new_mode) { > + case MAX2175_CSM_MODE_JUMP_FAST_TUNE: > + if (MAX2175_IS_BAND_AM(ctx)) { > + usleep_range(1250, 1500); /* 1.25ms */ > + } else { > + if (MAX2175_IS_DAB_MODE(ctx)) > + usleep_range(3000, 3500); /* 3ms */ > + else > + usleep_range(1250, 1500); /* 1.25ms */ > + } You can write this as if (MAX2175_IS_BAND_AM(ctx)) usleep_range(1250, 1500); /* 1.25ms */ else if (MAX2175_IS_DAB_MODE(ctx)) usleep_range(3000, 3500); /* 3ms */ else usleep_range(1250, 1500); /* 1.25ms */ > + break; > + > + /* Other mode switches can be added in the future if needed */ > + default: > + break; > + } > + > + ret = max2175_poll_csm_ready(ctx); > + if (ret) { > + v4l2_err(ctx->client, "csm did not settle: new mode %d\n", > + new_mode); > + return ret; > + } > + return ret; > +} [snip] > + > +static int max2175_csm_action(struct max2175_ctx *ctx, > + enum max2175_csm_mode action) > +{ > + int ret; > + int load_buffer = MAX2175_CSM_MODE_LOAD_TO_BUFFER; > + > + mxm_dbg(ctx, "csm action: %d\n", action); > + > + /* Perform one or two CSM mode commands. */ > + switch (action) { > + case MAX2175_CSM_MODE_NO_ACTION: > + /* Take no FSM Action. */ > + return 0; > + case MAX2175_CSM_MODE_LOAD_TO_BUFFER: > + case MAX2175_CSM_MODE_PRESET_TUNE: > + case MAX2175_CSM_MODE_SEARCH: > + case MAX2175_CSM_MODE_AF_UPDATE: > + case MAX2175_CSM_MODE_JUMP_FAST_TUNE: > + case MAX2175_CSM_MODE_CHECK: > + case MAX2175_CSM_MODE_LOAD_AND_SWAP: > + case MAX2175_CSM_MODE_END: > + ret = max2175_set_csm_mode(ctx, action); > + break; > + case MAX2175_CSM_MODE_BUFFER_PLUS_PRESET_TUNE: > + ret = max2175_set_csm_mode(ctx, load_buffer); > + if (ret) { > + v4l2_err(ctx->client, "csm action %d load buf failed\n", > + action); > + break; > + } > + ret = max2175_set_csm_mode(ctx, MAX2175_CSM_MODE_PRESET_TUNE); > + break; > + case MAX2175_CSM_MODE_BUFFER_PLUS_SEARCH: > + ret = max2175_set_csm_mode(ctx, load_buffer); > + if (ret) { > + v4l2_err(ctx->client, "csm action %d load buf failed\n", > + action); > + break; > + } Don't duplicate the error messages, move them after the switch statement. > + ret = max2175_set_csm_mode(ctx, MAX2175_CSM_MODE_SEARCH); > + break; > + case MAX2175_CSM_MODE_BUFFER_PLUS_AF_UPDATE: > + ret = max2175_set_csm_mode(ctx, load_buffer); > + if (ret) { > + v4l2_err(ctx->client, "csm action %d load buf failed\n", > + action); > + break; > + } > + ret = max2175_set_csm_mode(ctx, MAX2175_CSM_MODE_AF_UPDATE); > + break; > + case MAX2175_CSM_MODE_BUFFER_PLUS_JUMP_FAST_TUNE: This function is only called with action set to MAX2175_CSM_MODE_BUFFER_PLUS_JUMP_FAST_TUNE. I'd remove the rest of the code for now, unless you have a plan to use it soon. > + ret = max2175_set_csm_mode(ctx, load_buffer); > + if (ret) { > + v4l2_err(ctx->client, "csm action %d load buf failed\n", > + action); > + break; > + } > + ret = max2175_set_csm_mode(ctx, > + MAX2175_CSM_MODE_JUMP_FAST_TUNE); > + break; > + case MAX2175_CSM_MODE_BUFFER_PLUS_CHECK: > + ret = max2175_set_csm_mode(ctx, load_buffer); > + if (ret) { > + v4l2_err(ctx->client, "csm action %d load buf failed\n", > + action); > + break; > + } > + ret = max2175_set_csm_mode(ctx, MAX2175_CSM_MODE_CHECK); > + break; > + case MAX2175_CSM_MODE_BUFFER_PLUS_LOAD_AND_SWAP: > + ret = max2175_set_csm_mode(ctx, load_buffer); > + if (ret) { > + v4l2_err(ctx->client, "csm action %d load buf failed\n", > + action); > + break; > + } > + ret = max2175_set_csm_mode(ctx, MAX2175_CSM_MODE_LOAD_AND_SWAP); > + break; > + default: > + ret = max2175_set_csm_mode(ctx, MAX2175_CSM_MODE_NO_ACTION); > + break; > + } > + return ret; > +} > + > +static int max2175_set_lo_freq(struct max2175_ctx *ctx, u64 lo_freq) > +{ > + int ret; > + u32 lo_mult; > + u64 scaled_lo_freq; > + const u64 scale_factor = 1000000ULL; > + u64 scaled_npf, scaled_integer, scaled_fraction; > + u32 frac_desired, int_desired; > + u8 loband_bits, vcodiv_bits; Do you really support frequencies above 4GHz ? If not most of the 64-bit values could be stored in 32 bits. > + > + scaled_lo_freq = lo_freq; > + /* Scale to larger number for precision */ > + scaled_lo_freq = scaled_lo_freq * scale_factor * 100; > + > + mxm_dbg(ctx, "scaled lo_freq %llu lo_freq %llu\n", > + scaled_lo_freq, lo_freq); > + > + if (MAX2175_IS_BAND_AM(ctx)) { > + if (max2175_get_bit(ctx, 5, 7) == 0) > + loband_bits = 0; > + vcodiv_bits = 0; > + lo_mult = 16; > + } else if (max2175_get_bits(ctx, 5, 1, 0) == MAX2175_BAND_FM) { > + if (lo_freq <= 74700000) { > + loband_bits = 0; > + vcodiv_bits = 0; > + lo_mult = 16; > + } else if ((lo_freq > 74700000) && (lo_freq <= 110000000)) { No need for the inner parentheses. > + loband_bits = 1; > + vcodiv_bits = 0; > + } else { > + loband_bits = 1; > + vcodiv_bits = 3; > + } > + lo_mult = 8; > + } else if (max2175_get_bits(ctx, 5, 1, 0) == MAX2175_BAND_VHF) { > + if (lo_freq <= 210000000) { > + loband_bits = 2; > + vcodiv_bits = 2; > + } else { > + loband_bits = 2; > + vcodiv_bits = 1; > + } > + lo_mult = 4; > + } else { > + loband_bits = 3; > + vcodiv_bits = 2; > + lo_mult = 2; > + } > + > + if (max2175_get_bits(ctx, 5, 1, 0) == MAX2175_BAND_L) > + scaled_npf = (scaled_lo_freq / ctx->xtal_freq / lo_mult) / 100; > + else > + scaled_npf = (scaled_lo_freq / ctx->xtal_freq * lo_mult) / 100; > + > + scaled_integer = scaled_npf / scale_factor * scale_factor; > + int_desired = (u32)(scaled_npf / scale_factor); > + scaled_fraction = scaled_npf - scaled_integer; > + frac_desired = (u32)(scaled_fraction * 1048576 / scale_factor); > + > + /* Check CSM is not busy */ > + ret = max2175_poll_csm_ready(ctx); > + if (ret) { > + v4l2_err(ctx->client, "lo_freq: csm busy. freq %llu\n", > + lo_freq); > + return ret; > + } > + > + mxm_dbg(ctx, "loband %u vcodiv %u lo_mult %u scaled_npf %llu\n", > + loband_bits, vcodiv_bits, lo_mult, scaled_npf); > + mxm_dbg(ctx, "scaled int %llu frac %llu desired int %u frac %u\n", > + scaled_integer, scaled_fraction, int_desired, frac_desired); > + > + /* Write the calculated values to the appropriate registers */ > + max2175_set_bits(ctx, 5, 3, 2, loband_bits); > + max2175_set_bits(ctx, 6, 7, 6, vcodiv_bits); > + max2175_set_bits(ctx, 1, 7, 0, (u8)(int_desired & 0xff)); > + max2175_set_bits(ctx, 2, 3, 0, (u8)((frac_desired >> 16) & 0x1f)); > + max2175_set_bits(ctx, 3, 7, 0, (u8)((frac_desired >> 8) & 0xff)); > + max2175_set_bits(ctx, 4, 7, 0, (u8)(frac_desired & 0xff)); > + /* Flush the above registers to device */ > + max2175_flush_regstore(ctx, 1, 6); > + return ret; > +} [snip] > +static int max2175_set_rf_freq_non_am_bands(struct max2175_ctx *ctx, u64 > freq, > + u32 lo_pos) > +{ > + int ret; > + s64 adj_freq; > + u64 low_if_freq; > + > + 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_get_bits(ctx, 5, 1, 0) == MAX2175_BAND_VHF) { You perform this check in multiple places, you could create a helper function. > + if (lo_pos == MAX2175_LO_ABOVE_DESIRED) > + adj_freq = freq + low_if_freq; > + else > + adj_freq = freq - low_if_freq; > + } else { > + if (lo_pos == MAX2175_LO_ABOVE_DESIRED) > + adj_freq = freq - low_if_freq; > + else > + adj_freq = freq + low_if_freq; > + } This could be written if ((max2175_get_bits(ctx, 5, 1, 0) == MAX2175_BAND_VHF) == (lo_pos == MAX2175_LO_ABOVE_DESIRED)) adj_freq = freq + low_if_freq; else adj_freq = freq - low_if_freq; Same for the other similar constructs in the driver. Up to you. > + > + ret = max2175_set_lo_freq(ctx, adj_freq); > + if (ret) > + return ret; > + > + return max2175_set_nco_freq(ctx, low_if_freq); > +} [snip] > +#define max2175_ctx_from_sd(x) \ > + container_of(x, struct max2175_ctx, sd) > +#define max2175_ctx_from_ctrl(x) \ > + container_of(x, struct max2175_ctx, ctrl_hdl) I'd move it right after the structure definition, and turn them into static inline functions. > +static int max2175_s_ctrl(struct v4l2_ctrl *ctrl) > +{ > + struct max2175_ctx *ctx = max2175_ctx_from_ctrl(ctrl->handler); > + int ret = 0; > + > + mxm_dbg(ctx, "s_ctrl: id 0x%x, val %u\n", ctrl->id, ctrl->val); > + switch (ctrl->id) { > + case V4L2_CID_MAX2175_I2S_EN: > + max2175_i2s_enable(ctx, ctrl->val == 1); > + break; > + case V4L2_CID_MAX2175_I2S_MODE: > + max2175_s_ctrl_i2s_mode(ctx, ctrl->val); > + break; > + case V4L2_CID_MAX2175_AM_HIZ: > + v4l2_ctrl_activate(ctx->am_hiz, false); > + break; > + case V4L2_CID_MAX2175_HSLS: > + v4l2_ctrl_activate(ctx->hsls, false); > + break; > + case V4L2_CID_MAX2175_RX_MODE: > + mxm_dbg(ctx, "rx mode %u\n", ctrl->val); > + max2175_s_ctrl_rx_mode(ctx, ctrl->val); > + break; > + default: > + v4l2_err(ctx->client, "s:invalid ctrl id 0x%x\n", ctrl->id); > + ret = -EINVAL; This should never happen. The driver has too many error and debug messages overall. > + } > + > + return ret; > +} > + > +static int max2175_get_lna_gain(struct max2175_ctx *ctx) > +{ > + int gain = 0; > + enum max2175_band band = max2175_get_bits(ctx, 5, 1, 0); > + > + switch (band) { > + case MAX2175_BAND_AM: > + gain = max2175_read_bits(ctx, 51, 3, 1); > + break; > + case MAX2175_BAND_FM: > + gain = max2175_read_bits(ctx, 50, 3, 1); > + break; > + case MAX2175_BAND_VHF: > + gain = max2175_read_bits(ctx, 52, 3, 0); > + break; > + default: > + v4l2_err(ctx->client, "invalid band %d to get rf gain\n", band); Can this happen ? > + break; > + } > + return gain; > +} > + > +static int max2175_g_volatile_ctrl(struct v4l2_ctrl *ctrl) > +{ > + struct max2175_ctx *ctx = max2175_ctx_from_ctrl(ctrl->handler); > + > + /* Only the dynamically changing values need to be in g_volatile_ctrl */ > + mxm_dbg(ctx, "g_volatile_ctrl: id 0x%x\n", ctrl->id); > + 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; > + default: > + v4l2_err(ctx->client, "g:invalid ctrl id 0x%x\n", ctrl->id); This should never happen either. > + return -EINVAL; > + } > + mxm_dbg(ctx, "g_volatile_ctrl: id 0x%x val %d\n", ctrl->id, ctrl- >val); > + return 0; > +}; [snip] > +static const struct v4l2_ctrl_config max2175_i2s_en = { > + .ops = &max2175_ctrl_ops, > + .id = V4L2_CID_MAX2175_I2S_EN, V4L2_CID_MAX2175_I2S_ENABLE ? > + .name = "I2S Enable", > + .type = V4L2_CTRL_TYPE_BOOLEAN, > + .min = 0, > + .max = 1, > + .step = 1, > + .def = 1, > +}; > + > +static const struct v4l2_ctrl_config max2175_i2s_mode = { > + .ops = &max2175_ctrl_ops, > + .id = V4L2_CID_MAX2175_I2S_MODE, > + .name = "I2S_MODE value", > + .type = V4L2_CTRL_TYPE_INTEGER, Should this be a menu control ? > + .min = 0, > + .max = 4, > + .step = 1, > + .def = 0, > +}; > + > +static const struct v4l2_ctrl_config max2175_am_hiz = { > + .ops = &max2175_ctrl_ops, > + .id = V4L2_CID_MAX2175_AM_HIZ, > + .name = "AM High impedance input", > + .type = V4L2_CTRL_TYPE_BOOLEAN, > + .min = 0, > + .max = 1, > + .step = 1, > + .def = 0, > +}; > + > +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_INTEGER, > + .min = 0, > + .max = 1, > + .step = 1, > + .def = 1, > +}; > + > + > +/* NOTE: Any addition/deletion in the below list should be reflected in > + * max2175_modetag enum > + */ > +static const char * const max2175_ctrl_eu_rx_mode_strings[] = { > + "DAB 1.2", > + "NULL", Do you really mean "NULL", not NULL ? > +}; > + > +static const char * const max2175_ctrl_na_rx_mode_strings[] = { > + "NA FM 1.0", > + "NULL", > +}; > + > +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_mode_strings) - 2, If there's a single mode supported I'd skip adding those controls for now. > + .def = 0, > + .qmenu = max2175_ctrl_eu_rx_mode_strings, > +}; > + > +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_mode_strings) - 2, > + .def = 0, > + .qmenu = max2175_ctrl_na_rx_mode_strings, > +}; > + > +static u32 max2175_refout_load_to_bits(struct i2c_client *client, u32 load) > +{ > + u32 bits = 0; /* REFOUT disabled */ > + > + if (load >= 0 && load <= 40) > + bits = load / 10; > + else if (load >= 60 && load <= 70) > + bits = load / 10 - 1; > + else > + dev_warn(&client->dev, "invalid refout_load %u\n", load); Your DT bindings specify 0 as a valid value. An invalid value specified in DT should be a fatal error. > + > + return bits; > +} > + > +static int max2175_probe(struct i2c_client *client, > + const struct i2c_device_id *id) > +{ > + struct max2175_ctx *ctx; > + struct device *dev = &client->dev; > + struct v4l2_subdev *sd; > + struct v4l2_ctrl_handler *hdl; > + struct clk *clk; > + bool master = true; > + u32 refout_load, refout_bits = 0; /* REFOUT disabled */ > + int ret; > + > + /* Check if the adapter supports the needed features */ > + if (!i2c_check_functionality(client->adapter, > + I2C_FUNC_SMBUS_BYTE_DATA)) { > + dev_err(&client->dev, "i2c check failed\n"); > + return -EIO; > + } > + > + if (of_find_property(client->dev.of_node, "maxim,slave", NULL)) > + master = false; > + > + if (!of_property_read_u32(client->dev.of_node, "maxim,refout-load", > + &refout_load)) > + refout_bits = max2175_refout_load_to_bits(client, refout_load); > + > + clk = devm_clk_get(&client->dev, "xtal"); > + if (IS_ERR(clk)) { > + ret = PTR_ERR(clk); > + dev_err(&client->dev, "cannot get xtal clock %d\n", ret); > + return -ENODEV; > + } > + > + ctx = kzalloc(sizeof(struct max2175_ctx), sizeof(*ctx) > + GFP_KERNEL); This fits on one line. > + if (ctx == NULL) > + return -ENOMEM; > + > + sd = &ctx->sd; > + ctx->master = master; > + ctx->mode_resolved = false; > + > + /* Set the defaults */ > + ctx->freq = bands_rf.rangelow; > + > + ctx->xtal_freq = clk_get_rate(clk); > + dev_info(&client->dev, "xtal freq %lluHz\n", ctx->xtal_freq); > + > + v4l2_i2c_subdev_init(sd, client, &max2175_ops); > + ctx->client = client; > + > + sd->flags = V4L2_SUBDEV_FL_HAS_DEVNODE; > + ctx->dev = dev; > + > + /* Controls */ > + hdl = &ctx->ctrl_hdl; > + ret = v4l2_ctrl_handler_init(hdl, 8); > + if (ret) { > + dev_err(&client->dev, "ctrl handler init failed\n"); > + goto err; > + } > + > + ctx->lna_gain = v4l2_ctrl_new_std(hdl, &max2175_ctrl_ops, > + V4L2_CID_RF_TUNER_LNA_GAIN, > + 0, 15, 1, 2); > + 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->i2s_mode = v4l2_ctrl_new_custom(hdl, &max2175_i2s_mode, NULL); > + ctx->am_hiz = v4l2_ctrl_new_custom(hdl, &max2175_am_hiz, 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 = (struct max2175_rxmode *)eu_rx_modes; > + } else { > + ctx->rx_mode = v4l2_ctrl_new_custom(hdl, > + &max2175_na_rx_mode, NULL); > + ctx->rx_modes = (struct max2175_rxmode *)na_rx_modes; > + } > + ctx->sd.ctrl_handler = &ctx->ctrl_hdl; > + > + ret = v4l2_async_register_subdev(sd); > + if (ret) { > + dev_err(&client->dev, "register subdev failed\n"); > + goto err_reg; > + } > + dev_info(&client->dev, "subdev registered\n"); > + > + /* Initialize device */ > + ret = max2175_core_init(ctx, refout_bits); > + if (ret) > + goto err_init; > + > + mxm_dbg(ctx, "probed\n"); > + return 0; > + > +err_init: > + v4l2_async_unregister_subdev(sd); > +err_reg: > + v4l2_ctrl_handler_free(&ctx->ctrl_hdl); > +err: > + kfree(ctx); > + return ret; > +} > + > +static int max2175_remove(struct i2c_client *client) > +{ > + struct v4l2_subdev *sd = i2c_get_clientdata(client); > + struct max2175_ctx *ctx = max2175_ctx_from_sd(sd); > + > + v4l2_ctrl_handler_free(&ctx->ctrl_hdl); > + v4l2_async_unregister_subdev(sd); > + mxm_dbg(ctx, "removed\n"); > + kfree(ctx); > + return 0; > +} > + > +static const struct i2c_device_id max2175_id[] = { > + { DRIVER_NAME, 0}, > + {}, > +}; > + No need for a blank line here. > +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, > + }, > + .probe = max2175_probe, > + .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@xxxxxxxxxxxxxx>"); diff --git > a/drivers/media/i2c/max2175/max2175.h b/drivers/media/i2c/max2175/max2175.h > new file mode 100644 > index 0000000..61a508b > --- /dev/null > +++ b/drivers/media/i2c/max2175/max2175.h > @@ -0,0 +1,124 @@ > +/* > + * 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 chip. > + * > + * Copyright (C) 2016 Maxim Integrated Products > + * Copyright (C) 2016 Renesas Electronics Corporation > + * > + * 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. > + */ > + > +#ifndef __MAX2175_H__ > +#define __MAX2175_H__ > + > +#include <linux/types.h> > + > +enum max2175_region { > + MAX2175_REGION_EU = 0, /* Europe */ > + MAX2175_REGION_NA, /* North America */ > +}; > + > +#define MAX2175_EU_XTAL_FREQ (36864000) /* In Hz */ > +#define MAX2175_NA_XTAL_FREQ (40186125) /* In Hz */ > + > +enum max2175_band { > + MAX2175_BAND_AM = 0, > + MAX2175_BAND_FM, > + MAX2175_BAND_VHF, > + MAX2175_BAND_L, > +}; > + > +/* NOTE: Any addition/deletion in the below enum should be reflected in > + * V4L2_CID_MAX2175_RX_MODE ctrl strings > + */ > +enum max2175_modetag { > + /* EU modes */ > + MAX2175_DAB_1_2 = 0, > + > + /* Other possible modes to add in future > + * MAX2175_DAB_1_0, > + * MAX2175_DAB_1_3, > + * MAX2175_EU_FM_2_2, > + * MAX2175_EU_FM_1_0, > + * MAX2175_EU_FMHD_4_0, > + * MAX2175_EU_AM_1_0, > + * MAX2175_EU_AM_2_2, > + */ > + > + /* NA modes */ > + MAX2175_NA_FM_1_0 = 0, > + > + /* Other possible modes to add in future > + * MAX2175_NA_FM_1_2, > + * MAX2175_NA_FMHD_1_0, > + * MAX2175_NA_FMHD_1_2, > + * MAX2175_NA_AM_1_0, > + * MAX2175_NA_AM_1_2, > + */ > +}; > + > +/* Supported I2S modes */ > +enum { > + MAX2175_I2S_MODE0 = 0, > + MAX2175_I2S_MODE1, > + MAX2175_I2S_MODE2, > + MAX2175_I2S_MODE3, > + MAX2175_I2S_MODE4, > +}; > + > +/* Coefficient table groups */ > +enum { > + MAX2175_CH_MSEL = 0, > + MAX2175_EQ_MSEL, > + MAX2175_AA_MSEL, > +}; > + > +/* HSLS LO injection polarity */ > +enum { > + MAX2175_LO_BELOW_DESIRED = 0, > + MAX2175_LO_ABOVE_DESIRED, > +}; > + > +/* Channel FSM modes */ > +enum max2175_csm_mode { > + MAX2175_CSM_MODE_LOAD_TO_BUFFER = 0, > + MAX2175_CSM_MODE_PRESET_TUNE, > + MAX2175_CSM_MODE_SEARCH, > + MAX2175_CSM_MODE_AF_UPDATE, > + MAX2175_CSM_MODE_JUMP_FAST_TUNE, > + MAX2175_CSM_MODE_CHECK, > + MAX2175_CSM_MODE_LOAD_AND_SWAP, > + MAX2175_CSM_MODE_END, > + MAX2175_CSM_MODE_BUFFER_PLUS_PRESET_TUNE, > + MAX2175_CSM_MODE_BUFFER_PLUS_SEARCH, > + MAX2175_CSM_MODE_BUFFER_PLUS_AF_UPDATE, > + MAX2175_CSM_MODE_BUFFER_PLUS_JUMP_FAST_TUNE, > + MAX2175_CSM_MODE_BUFFER_PLUS_CHECK, > + MAX2175_CSM_MODE_BUFFER_PLUS_LOAD_AND_SWAP, > + MAX2175_CSM_MODE_NO_ACTION > +}; > + > +/* Rx mode */ > +struct max2175_rxmode { > + enum max2175_band band; /* Associated band */ > + u32 freq; /* Default freq in Hz */ > + u8 i2s_word_size; /* Bit value */ > + u8 i2s_modes[4]; /* Supported modes */ > +}; > + > +/* Register map */ > +struct max2175_regmap { > + u8 idx; /* Register index */ > + u8 val; /* Register value */ > +}; As no other file than max2175.c includes this, I would move at least the structure definitions to the .c file. > +#endif /* __MAX2175_H__ */ -- Regards, Laurent Pinchart -- To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html