1 // SPDX-License-Identifier: GPL-2.0-or-later
3 TDA8261 8PSK/QPSK tuner driver
4 Copyright (C) Manu Abraham (abraham.manu@gmail.com)
9 #include <linux/init.h>
10 #include <linux/kernel.h>
11 #include <linux/module.h>
12 #include <linux/slab.h>
14 #include <media/dvb_frontend.h>
17 struct tda8261_state
{
18 struct dvb_frontend
*fe
;
19 struct i2c_adapter
*i2c
;
20 const struct tda8261_config
*config
;
27 static int tda8261_read(struct tda8261_state
*state
, u8
*buf
)
29 const struct tda8261_config
*config
= state
->config
;
31 struct i2c_msg msg
= { .addr
= config
->addr
, .flags
= I2C_M_RD
,.buf
= buf
, .len
= 1 };
33 if ((err
= i2c_transfer(state
->i2c
, &msg
, 1)) != 1)
34 pr_err("%s: read error, err=%d\n", __func__
, err
);
39 static int tda8261_write(struct tda8261_state
*state
, u8
*buf
)
41 const struct tda8261_config
*config
= state
->config
;
43 struct i2c_msg msg
= { .addr
= config
->addr
, .flags
= 0, .buf
= buf
, .len
= 4 };
45 if ((err
= i2c_transfer(state
->i2c
, &msg
, 1)) != 1)
46 pr_err("%s: write error, err=%d\n", __func__
, err
);
51 static int tda8261_get_status(struct dvb_frontend
*fe
, u32
*status
)
53 struct tda8261_state
*state
= fe
->tuner_priv
;
59 if ((err
= tda8261_read(state
, &result
)) < 0) {
60 pr_err("%s: I/O Error\n", __func__
);
63 if ((result
>> 6) & 0x01) {
64 pr_debug("%s: Tuner Phase Locked\n", __func__
);
71 static const u32 div_tab
[] = { 2000, 1000, 500, 250, 125 }; /* kHz */
72 static const u8 ref_div
[] = { 0x00, 0x01, 0x02, 0x05, 0x07 };
74 static int tda8261_get_frequency(struct dvb_frontend
*fe
, u32
*frequency
)
76 struct tda8261_state
*state
= fe
->tuner_priv
;
78 *frequency
= state
->frequency
;
83 static int tda8261_set_params(struct dvb_frontend
*fe
)
85 struct dtv_frontend_properties
*c
= &fe
->dtv_property_cache
;
86 struct tda8261_state
*state
= fe
->tuner_priv
;
87 const struct tda8261_config
*config
= state
->config
;
88 u32 frequency
, N
, status
= 0;
93 * N = Max VCO Frequency / Channel Spacing
94 * Max VCO Frequency = VCO frequency + (channel spacing - 1)
95 * (to account for half channel spacing on either side)
97 frequency
= c
->frequency
;
98 if ((frequency
< 950000) || (frequency
> 2150000)) {
99 pr_warn("%s: Frequency beyond limits, frequency=%d\n",
100 __func__
, frequency
);
103 N
= (frequency
+ (div_tab
[config
->step_size
] - 1)) / div_tab
[config
->step_size
];
104 pr_debug("%s: Step size=%d, Divider=%d, PG=0x%02x (%d)\n",
105 __func__
, config
->step_size
, div_tab
[config
->step_size
], N
, N
);
107 buf
[0] = (N
>> 8) & 0xff;
109 buf
[2] = (0x01 << 7) | ((ref_div
[config
->step_size
] & 0x07) << 1);
111 if (frequency
< 1450000)
113 else if (frequency
< 2000000)
115 else if (frequency
< 2150000)
119 err
= tda8261_write(state
, buf
);
121 pr_err("%s: I/O Error\n", __func__
);
124 /* sleep for some time */
125 pr_debug("%s: Waiting to Phase LOCK\n", __func__
);
128 if ((err
= tda8261_get_status(fe
, &status
)) < 0) {
129 pr_err("%s: I/O Error\n", __func__
);
133 pr_debug("%s: Tuner Phase locked: status=%d\n", __func__
,
135 state
->frequency
= frequency
; /* cache successful state */
137 pr_debug("%s: No Phase lock: status=%d\n", __func__
, status
);
143 static void tda8261_release(struct dvb_frontend
*fe
)
145 struct tda8261_state
*state
= fe
->tuner_priv
;
147 fe
->tuner_priv
= NULL
;
151 static const struct dvb_tuner_ops tda8261_ops
= {
155 .frequency_min_hz
= 950 * MHz
,
156 .frequency_max_hz
= 2150 * MHz
,
159 .set_params
= tda8261_set_params
,
160 .get_frequency
= tda8261_get_frequency
,
161 .get_status
= tda8261_get_status
,
162 .release
= tda8261_release
165 struct dvb_frontend
*tda8261_attach(struct dvb_frontend
*fe
,
166 const struct tda8261_config
*config
,
167 struct i2c_adapter
*i2c
)
169 struct tda8261_state
*state
= NULL
;
171 if ((state
= kzalloc(sizeof (struct tda8261_state
), GFP_KERNEL
)) == NULL
)
174 state
->config
= config
;
177 fe
->tuner_priv
= state
;
178 fe
->ops
.tuner_ops
= tda8261_ops
;
180 fe
->ops
.tuner_ops
.info
.frequency_step_hz
= div_tab
[config
->step_size
] * kHz
;
182 pr_info("%s: Attaching TDA8261 8PSK/QPSK tuner\n", __func__
);
191 EXPORT_SYMBOL(tda8261_attach
);
193 MODULE_AUTHOR("Manu Abraham");
194 MODULE_DESCRIPTION("TDA8261 8PSK/QPSK Tuner");
195 MODULE_LICENSE("GPL");