18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Allegro A8293 SEC driver
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2011 Antti Palosaari <crope@iki.fi>
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include "a8293.h"
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_cistruct a8293_dev {
118c2ecf20Sopenharmony_ci	struct i2c_client *client;
128c2ecf20Sopenharmony_ci	u8 reg[2];
138c2ecf20Sopenharmony_ci};
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_cistatic int a8293_set_voltage(struct dvb_frontend *fe,
168c2ecf20Sopenharmony_ci			     enum fe_sec_voltage fe_sec_voltage)
178c2ecf20Sopenharmony_ci{
188c2ecf20Sopenharmony_ci	struct a8293_dev *dev = fe->sec_priv;
198c2ecf20Sopenharmony_ci	struct i2c_client *client = dev->client;
208c2ecf20Sopenharmony_ci	int ret;
218c2ecf20Sopenharmony_ci	u8 reg0, reg1;
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci	dev_dbg(&client->dev, "fe_sec_voltage=%d\n", fe_sec_voltage);
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci	switch (fe_sec_voltage) {
268c2ecf20Sopenharmony_ci	case SEC_VOLTAGE_OFF:
278c2ecf20Sopenharmony_ci		/* ENB=0 */
288c2ecf20Sopenharmony_ci		reg0 = 0x10;
298c2ecf20Sopenharmony_ci		break;
308c2ecf20Sopenharmony_ci	case SEC_VOLTAGE_13:
318c2ecf20Sopenharmony_ci		/* VSEL0=1, VSEL1=0, VSEL2=0, VSEL3=0, ENB=1*/
328c2ecf20Sopenharmony_ci		reg0 = 0x31;
338c2ecf20Sopenharmony_ci		break;
348c2ecf20Sopenharmony_ci	case SEC_VOLTAGE_18:
358c2ecf20Sopenharmony_ci		/* VSEL0=0, VSEL1=0, VSEL2=0, VSEL3=1, ENB=1*/
368c2ecf20Sopenharmony_ci		reg0 = 0x38;
378c2ecf20Sopenharmony_ci		break;
388c2ecf20Sopenharmony_ci	default:
398c2ecf20Sopenharmony_ci		ret = -EINVAL;
408c2ecf20Sopenharmony_ci		goto err;
418c2ecf20Sopenharmony_ci	}
428c2ecf20Sopenharmony_ci	if (reg0 != dev->reg[0]) {
438c2ecf20Sopenharmony_ci		ret = i2c_master_send(client, &reg0, 1);
448c2ecf20Sopenharmony_ci		if (ret < 0)
458c2ecf20Sopenharmony_ci			goto err;
468c2ecf20Sopenharmony_ci		dev->reg[0] = reg0;
478c2ecf20Sopenharmony_ci	}
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci	/* TMODE=0, TGATE=1 */
508c2ecf20Sopenharmony_ci	reg1 = 0x82;
518c2ecf20Sopenharmony_ci	if (reg1 != dev->reg[1]) {
528c2ecf20Sopenharmony_ci		ret = i2c_master_send(client, &reg1, 1);
538c2ecf20Sopenharmony_ci		if (ret < 0)
548c2ecf20Sopenharmony_ci			goto err;
558c2ecf20Sopenharmony_ci		dev->reg[1] = reg1;
568c2ecf20Sopenharmony_ci	}
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci	usleep_range(1500, 50000);
598c2ecf20Sopenharmony_ci	return 0;
608c2ecf20Sopenharmony_cierr:
618c2ecf20Sopenharmony_ci	dev_dbg(&client->dev, "failed=%d\n", ret);
628c2ecf20Sopenharmony_ci	return ret;
638c2ecf20Sopenharmony_ci}
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_cistatic int a8293_probe(struct i2c_client *client,
668c2ecf20Sopenharmony_ci		       const struct i2c_device_id *id)
678c2ecf20Sopenharmony_ci{
688c2ecf20Sopenharmony_ci	struct a8293_dev *dev;
698c2ecf20Sopenharmony_ci	struct a8293_platform_data *pdata = client->dev.platform_data;
708c2ecf20Sopenharmony_ci	struct dvb_frontend *fe = pdata->dvb_frontend;
718c2ecf20Sopenharmony_ci	int ret;
728c2ecf20Sopenharmony_ci	u8 buf[2];
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
758c2ecf20Sopenharmony_ci	if (!dev) {
768c2ecf20Sopenharmony_ci		ret = -ENOMEM;
778c2ecf20Sopenharmony_ci		goto err;
788c2ecf20Sopenharmony_ci	}
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci	dev->client = client;
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci	/* check if the SEC is there */
838c2ecf20Sopenharmony_ci	ret = i2c_master_recv(client, buf, 2);
848c2ecf20Sopenharmony_ci	if (ret < 0)
858c2ecf20Sopenharmony_ci		goto err_kfree;
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci	/* override frontend ops */
888c2ecf20Sopenharmony_ci	fe->ops.set_voltage = a8293_set_voltage;
898c2ecf20Sopenharmony_ci	fe->sec_priv = dev;
908c2ecf20Sopenharmony_ci	i2c_set_clientdata(client, dev);
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci	dev_info(&client->dev, "Allegro A8293 SEC successfully attached\n");
938c2ecf20Sopenharmony_ci	return 0;
948c2ecf20Sopenharmony_cierr_kfree:
958c2ecf20Sopenharmony_ci	kfree(dev);
968c2ecf20Sopenharmony_cierr:
978c2ecf20Sopenharmony_ci	dev_dbg(&client->dev, "failed=%d\n", ret);
988c2ecf20Sopenharmony_ci	return ret;
998c2ecf20Sopenharmony_ci}
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_cistatic int a8293_remove(struct i2c_client *client)
1028c2ecf20Sopenharmony_ci{
1038c2ecf20Sopenharmony_ci	struct a8293_dev *dev = i2c_get_clientdata(client);
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci	dev_dbg(&client->dev, "\n");
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	kfree(dev);
1088c2ecf20Sopenharmony_ci	return 0;
1098c2ecf20Sopenharmony_ci}
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_cistatic const struct i2c_device_id a8293_id_table[] = {
1128c2ecf20Sopenharmony_ci	{"a8293", 0},
1138c2ecf20Sopenharmony_ci	{}
1148c2ecf20Sopenharmony_ci};
1158c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, a8293_id_table);
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_cistatic struct i2c_driver a8293_driver = {
1188c2ecf20Sopenharmony_ci	.driver = {
1198c2ecf20Sopenharmony_ci		.name	= "a8293",
1208c2ecf20Sopenharmony_ci		.suppress_bind_attrs = true,
1218c2ecf20Sopenharmony_ci	},
1228c2ecf20Sopenharmony_ci	.probe		= a8293_probe,
1238c2ecf20Sopenharmony_ci	.remove		= a8293_remove,
1248c2ecf20Sopenharmony_ci	.id_table	= a8293_id_table,
1258c2ecf20Sopenharmony_ci};
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_cimodule_i2c_driver(a8293_driver);
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ciMODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
1308c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Allegro A8293 SEC driver");
1318c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
132