.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
---|
1 | 2 | /* |
---|
2 | 3 | STV0900/0903 Multistandard Broadcast Frontend driver |
---|
3 | 4 | Copyright (C) Manu Abraham <abraham.manu@gmail.com> |
---|
4 | 5 | |
---|
5 | 6 | Copyright (C) ST Microelectronics |
---|
6 | 7 | |
---|
7 | | - This program is free software; you can redistribute it and/or modify |
---|
8 | | - it under the terms of the GNU General Public License as published by |
---|
9 | | - the Free Software Foundation; either version 2 of the License, or |
---|
10 | | - (at your option) any later version. |
---|
11 | | - |
---|
12 | | - This program is distributed in the hope that it will be useful, |
---|
13 | | - but WITHOUT ANY WARRANTY; without even the implied warranty of |
---|
14 | | - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
---|
15 | | - GNU General Public License for more details. |
---|
16 | | - |
---|
17 | | - You should have received a copy of the GNU General Public License |
---|
18 | | - along with this program; if not, write to the Free Software |
---|
19 | | - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
---|
20 | 8 | */ |
---|
21 | 9 | |
---|
22 | 10 | #include <linux/init.h> |
---|
.. | .. |
---|
4901 | 4889 | return stv090x_write_reg(state, STV090x_GPIOxCFG(gpio), reg); |
---|
4902 | 4890 | } |
---|
4903 | 4891 | |
---|
| 4892 | +static int stv090x_setup_compound(struct stv090x_state *state) |
---|
| 4893 | +{ |
---|
| 4894 | + struct stv090x_dev *temp_int; |
---|
| 4895 | + |
---|
| 4896 | + temp_int = find_dev(state->i2c, |
---|
| 4897 | + state->config->address); |
---|
| 4898 | + |
---|
| 4899 | + if (temp_int && state->demod_mode == STV090x_DUAL) { |
---|
| 4900 | + state->internal = temp_int->internal; |
---|
| 4901 | + state->internal->num_used++; |
---|
| 4902 | + dprintk(FE_INFO, 1, "Found Internal Structure!"); |
---|
| 4903 | + } else { |
---|
| 4904 | + state->internal = kmalloc(sizeof(*state->internal), GFP_KERNEL); |
---|
| 4905 | + if (!state->internal) |
---|
| 4906 | + goto error; |
---|
| 4907 | + temp_int = append_internal(state->internal); |
---|
| 4908 | + if (!temp_int) { |
---|
| 4909 | + kfree(state->internal); |
---|
| 4910 | + goto error; |
---|
| 4911 | + } |
---|
| 4912 | + state->internal->num_used = 1; |
---|
| 4913 | + state->internal->mclk = 0; |
---|
| 4914 | + state->internal->dev_ver = 0; |
---|
| 4915 | + state->internal->i2c_adap = state->i2c; |
---|
| 4916 | + state->internal->i2c_addr = state->config->address; |
---|
| 4917 | + dprintk(FE_INFO, 1, "Create New Internal Structure!"); |
---|
| 4918 | + |
---|
| 4919 | + mutex_init(&state->internal->demod_lock); |
---|
| 4920 | + mutex_init(&state->internal->tuner_lock); |
---|
| 4921 | + |
---|
| 4922 | + if (stv090x_setup(&state->frontend) < 0) { |
---|
| 4923 | + dprintk(FE_ERROR, 1, "Error setting up device"); |
---|
| 4924 | + goto err_remove; |
---|
| 4925 | + } |
---|
| 4926 | + } |
---|
| 4927 | + |
---|
| 4928 | + if (state->internal->dev_ver >= 0x30) |
---|
| 4929 | + state->frontend.ops.info.caps |= FE_CAN_MULTISTREAM; |
---|
| 4930 | + |
---|
| 4931 | + /* workaround for stuck DiSEqC output */ |
---|
| 4932 | + if (state->config->diseqc_envelope_mode) |
---|
| 4933 | + stv090x_send_diseqc_burst(&state->frontend, SEC_MINI_A); |
---|
| 4934 | + |
---|
| 4935 | + state->config->set_gpio = stv090x_set_gpio; |
---|
| 4936 | + |
---|
| 4937 | + dprintk(FE_ERROR, 1, "Probing %s demodulator(%d) Cut=0x%02x", |
---|
| 4938 | + state->device == STV0900 ? "STV0900" : "STV0903", |
---|
| 4939 | + state->config->demod, |
---|
| 4940 | + state->internal->dev_ver); |
---|
| 4941 | + |
---|
| 4942 | + return 0; |
---|
| 4943 | + |
---|
| 4944 | +error: |
---|
| 4945 | + return -ENOMEM; |
---|
| 4946 | +err_remove: |
---|
| 4947 | + remove_dev(state->internal); |
---|
| 4948 | + kfree(state->internal); |
---|
| 4949 | + return -ENODEV; |
---|
| 4950 | +} |
---|
| 4951 | + |
---|
4904 | 4952 | static const struct dvb_frontend_ops stv090x_ops = { |
---|
4905 | 4953 | .delsys = { SYS_DVBS, SYS_DVBS2, SYS_DSS }, |
---|
4906 | 4954 | .info = { |
---|
.. | .. |
---|
4933 | 4981 | .read_snr = stv090x_read_cnr, |
---|
4934 | 4982 | }; |
---|
4935 | 4983 | |
---|
| 4984 | +static struct dvb_frontend *stv090x_get_dvb_frontend(struct i2c_client *client) |
---|
| 4985 | +{ |
---|
| 4986 | + struct stv090x_state *state = i2c_get_clientdata(client); |
---|
| 4987 | + |
---|
| 4988 | + dev_dbg(&client->dev, "\n"); |
---|
| 4989 | + |
---|
| 4990 | + return &state->frontend; |
---|
| 4991 | +} |
---|
| 4992 | + |
---|
| 4993 | +static int stv090x_probe(struct i2c_client *client, |
---|
| 4994 | + const struct i2c_device_id *id) |
---|
| 4995 | +{ |
---|
| 4996 | + int ret = 0; |
---|
| 4997 | + struct stv090x_config *config = client->dev.platform_data; |
---|
| 4998 | + |
---|
| 4999 | + struct stv090x_state *state = NULL; |
---|
| 5000 | + |
---|
| 5001 | + state = kzalloc(sizeof(*state), GFP_KERNEL); |
---|
| 5002 | + if (!state) { |
---|
| 5003 | + ret = -ENOMEM; |
---|
| 5004 | + goto error; |
---|
| 5005 | + } |
---|
| 5006 | + |
---|
| 5007 | + state->verbose = &verbose; |
---|
| 5008 | + state->config = config; |
---|
| 5009 | + state->i2c = client->adapter; |
---|
| 5010 | + state->frontend.ops = stv090x_ops; |
---|
| 5011 | + state->frontend.demodulator_priv = state; |
---|
| 5012 | + state->demod = config->demod; |
---|
| 5013 | + /* Single or Dual mode */ |
---|
| 5014 | + state->demod_mode = config->demod_mode; |
---|
| 5015 | + state->device = config->device; |
---|
| 5016 | + /* default */ |
---|
| 5017 | + state->rolloff = STV090x_RO_35; |
---|
| 5018 | + |
---|
| 5019 | + ret = stv090x_setup_compound(state); |
---|
| 5020 | + if (ret) |
---|
| 5021 | + goto error; |
---|
| 5022 | + |
---|
| 5023 | + i2c_set_clientdata(client, state); |
---|
| 5024 | + |
---|
| 5025 | + /* setup callbacks */ |
---|
| 5026 | + config->get_dvb_frontend = stv090x_get_dvb_frontend; |
---|
| 5027 | + |
---|
| 5028 | + return 0; |
---|
| 5029 | + |
---|
| 5030 | +error: |
---|
| 5031 | + kfree(state); |
---|
| 5032 | + return ret; |
---|
| 5033 | +} |
---|
| 5034 | + |
---|
| 5035 | +static int stv090x_remove(struct i2c_client *client) |
---|
| 5036 | +{ |
---|
| 5037 | + struct stv090x_state *state = i2c_get_clientdata(client); |
---|
| 5038 | + |
---|
| 5039 | + stv090x_release(&state->frontend); |
---|
| 5040 | + return 0; |
---|
| 5041 | +} |
---|
4936 | 5042 | |
---|
4937 | 5043 | struct dvb_frontend *stv090x_attach(struct stv090x_config *config, |
---|
4938 | 5044 | struct i2c_adapter *i2c, |
---|
4939 | 5045 | enum stv090x_demodulator demod) |
---|
4940 | 5046 | { |
---|
| 5047 | + int ret = 0; |
---|
4941 | 5048 | struct stv090x_state *state = NULL; |
---|
4942 | | - struct stv090x_dev *temp_int; |
---|
4943 | 5049 | |
---|
4944 | | - state = kzalloc(sizeof (struct stv090x_state), GFP_KERNEL); |
---|
4945 | | - if (state == NULL) |
---|
| 5050 | + state = kzalloc(sizeof(*state), GFP_KERNEL); |
---|
| 5051 | + if (!state) |
---|
4946 | 5052 | goto error; |
---|
4947 | 5053 | |
---|
4948 | 5054 | state->verbose = &verbose; |
---|
.. | .. |
---|
4951 | 5057 | state->frontend.ops = stv090x_ops; |
---|
4952 | 5058 | state->frontend.demodulator_priv = state; |
---|
4953 | 5059 | state->demod = demod; |
---|
4954 | | - state->demod_mode = config->demod_mode; /* Single or Dual mode */ |
---|
| 5060 | + /* Single or Dual mode */ |
---|
| 5061 | + state->demod_mode = config->demod_mode; |
---|
4955 | 5062 | state->device = config->device; |
---|
4956 | | - state->rolloff = STV090x_RO_35; /* default */ |
---|
| 5063 | + /* default */ |
---|
| 5064 | + state->rolloff = STV090x_RO_35; |
---|
4957 | 5065 | |
---|
4958 | | - temp_int = find_dev(state->i2c, |
---|
4959 | | - state->config->address); |
---|
4960 | | - |
---|
4961 | | - if ((temp_int != NULL) && (state->demod_mode == STV090x_DUAL)) { |
---|
4962 | | - state->internal = temp_int->internal; |
---|
4963 | | - state->internal->num_used++; |
---|
4964 | | - dprintk(FE_INFO, 1, "Found Internal Structure!"); |
---|
4965 | | - } else { |
---|
4966 | | - state->internal = kmalloc(sizeof(struct stv090x_internal), |
---|
4967 | | - GFP_KERNEL); |
---|
4968 | | - if (!state->internal) |
---|
4969 | | - goto error; |
---|
4970 | | - temp_int = append_internal(state->internal); |
---|
4971 | | - if (!temp_int) { |
---|
4972 | | - kfree(state->internal); |
---|
4973 | | - goto error; |
---|
4974 | | - } |
---|
4975 | | - state->internal->num_used = 1; |
---|
4976 | | - state->internal->mclk = 0; |
---|
4977 | | - state->internal->dev_ver = 0; |
---|
4978 | | - state->internal->i2c_adap = state->i2c; |
---|
4979 | | - state->internal->i2c_addr = state->config->address; |
---|
4980 | | - dprintk(FE_INFO, 1, "Create New Internal Structure!"); |
---|
4981 | | - |
---|
4982 | | - mutex_init(&state->internal->demod_lock); |
---|
4983 | | - mutex_init(&state->internal->tuner_lock); |
---|
4984 | | - |
---|
4985 | | - if (stv090x_setup(&state->frontend) < 0) { |
---|
4986 | | - dprintk(FE_ERROR, 1, "Error setting up device"); |
---|
4987 | | - goto err_remove; |
---|
4988 | | - } |
---|
4989 | | - } |
---|
4990 | | - |
---|
4991 | | - if (state->internal->dev_ver >= 0x30) |
---|
4992 | | - state->frontend.ops.info.caps |= FE_CAN_MULTISTREAM; |
---|
4993 | | - |
---|
4994 | | - /* workaround for stuck DiSEqC output */ |
---|
4995 | | - if (config->diseqc_envelope_mode) |
---|
4996 | | - stv090x_send_diseqc_burst(&state->frontend, SEC_MINI_A); |
---|
4997 | | - |
---|
4998 | | - config->set_gpio = stv090x_set_gpio; |
---|
4999 | | - |
---|
5000 | | - dprintk(FE_ERROR, 1, "Attaching %s demodulator(%d) Cut=0x%02x", |
---|
5001 | | - state->device == STV0900 ? "STV0900" : "STV0903", |
---|
5002 | | - demod, |
---|
5003 | | - state->internal->dev_ver); |
---|
| 5066 | + ret = stv090x_setup_compound(state); |
---|
| 5067 | + if (ret) |
---|
| 5068 | + goto error; |
---|
5004 | 5069 | |
---|
5005 | 5070 | return &state->frontend; |
---|
5006 | 5071 | |
---|
5007 | | -err_remove: |
---|
5008 | | - remove_dev(state->internal); |
---|
5009 | | - kfree(state->internal); |
---|
5010 | 5072 | error: |
---|
5011 | 5073 | kfree(state); |
---|
5012 | 5074 | return NULL; |
---|
5013 | 5075 | } |
---|
5014 | 5076 | EXPORT_SYMBOL(stv090x_attach); |
---|
| 5077 | + |
---|
| 5078 | +static const struct i2c_device_id stv090x_id_table[] = { |
---|
| 5079 | + {"stv090x", 0}, |
---|
| 5080 | + {} |
---|
| 5081 | +}; |
---|
| 5082 | +MODULE_DEVICE_TABLE(i2c, stv090x_id_table); |
---|
| 5083 | + |
---|
| 5084 | +static struct i2c_driver stv090x_driver = { |
---|
| 5085 | + .driver = { |
---|
| 5086 | + .name = "stv090x", |
---|
| 5087 | + .suppress_bind_attrs = true, |
---|
| 5088 | + }, |
---|
| 5089 | + .probe = stv090x_probe, |
---|
| 5090 | + .remove = stv090x_remove, |
---|
| 5091 | + .id_table = stv090x_id_table, |
---|
| 5092 | +}; |
---|
| 5093 | + |
---|
| 5094 | +module_i2c_driver(stv090x_driver); |
---|
| 5095 | + |
---|
5015 | 5096 | MODULE_PARM_DESC(verbose, "Set Verbosity level"); |
---|
5016 | 5097 | MODULE_AUTHOR("Manu Abraham"); |
---|
5017 | 5098 | MODULE_DESCRIPTION("STV090x Multi-Std Broadcast frontend"); |
---|