/* * drivers/video/ds90uh925q_ser.c * FPDLink Serializer driver * * Copyright (C) 2012 NVIDIA CORPORATION. All rights reserved. * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and * may be copied, distributed, and modified under those terms. * * 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 #include #include #include #include #include #include #include #define LVDS_SER_REG_CONFIG_1 0x4 #define LVDS_SER_REG_CONFIG_1_BKWD_OVERRIDE 3 #define LVDS_SER_REG_CONFIG_1_BKWD 2 #define LVDS_SER_REG_DATA_PATH_CTRL 0x12 #define LVDS_SER_REG_DATA_PATH_CTRL_PASS_RGB 6 #define LVDS_SER_REG_CONFIG_0 0x3 #define LVDS_SER_REG_CONFIG_0_TRFB 0 struct ds90uh925q_rec { struct ds90uh925q_platform_data *pdata; }; static int ser_i2c_read(struct i2c_client *client, int reg, uint8_t *val) { int ret; ret = i2c_smbus_read_byte_data(client, reg); if (ret < 0) { dev_err(&client->dev, "failed reading at 0x%02x\n", reg); return ret; } *val = ret; return 0; } static int ser_i2c_write(struct i2c_client *client, u8 reg, u8 val) { int ret = i2c_smbus_write_byte_data(client, reg, val); if (ret) dev_err(&client->dev, "failed to write\n"); return ret; } static int lvds_ser_config(struct i2c_client *client, bool is_fpdlinkII, bool support_hdcp, bool clk_rise_edge) { u8 val; int err = 0; if (is_fpdlinkII) { err = ser_i2c_read(client, LVDS_SER_REG_CONFIG_1, &val); if (err < 0) return err; val |= (1 << LVDS_SER_REG_CONFIG_1_BKWD_OVERRIDE); val |= (1 << LVDS_SER_REG_CONFIG_1_BKWD); err = ser_i2c_write(client, LVDS_SER_REG_CONFIG_1, val); if (err < 0) return err; } else if (!support_hdcp) { err = ser_i2c_read(client, LVDS_SER_REG_DATA_PATH_CTRL, &val); if (err < 0) return err; val |= (1 << LVDS_SER_REG_DATA_PATH_CTRL_PASS_RGB); err = ser_i2c_write(client, LVDS_SER_REG_DATA_PATH_CTRL, val); if (err < 0) return err; } if (clk_rise_edge) { err = ser_i2c_read(client, LVDS_SER_REG_CONFIG_0, &val); if (err < 0) return err; val |= (1 << LVDS_SER_REG_CONFIG_0_TRFB); err = ser_i2c_write(client, LVDS_SER_REG_CONFIG_0, val); } return (err < 0 ? err : 0); } static int ds90uh925q_enable(struct i2c_client *client) { struct ds90uh925q_rec *data = i2c_get_clientdata(client); int err; /* Turn on serializer chip */ if (data->pdata->has_lvds_en_gpio) gpio_set_value(data->pdata->lvds_en_gpio, 1); err = lvds_ser_config(client, data->pdata->is_fpdlinkII, data->pdata->support_hdcp, data->pdata->clk_rise_edge); if (err) pr_err("%s: lvds failed\n", __func__); return err; } static void ds90uh925q_disable(struct i2c_client *client) { struct ds90uh925q_rec *data = i2c_get_clientdata(client); /* Turn off serializer chip */ if (data->pdata->has_lvds_en_gpio) gpio_set_value(data->pdata->lvds_en_gpio, 0); } static int ds90uh925q_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct ds90uh925q_rec *data; struct ds90uh925q_platform_data *pdata = client->dev.platform_data; int err; if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { dev_err(&client->dev, "SMBUS Byte Data not Supported\n"); return -EIO; } if (!pdata) { dev_err(&client->dev, "no platform data?\n"); return -EINVAL; } data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL); if (data == NULL) return -ENOMEM; data->pdata = pdata; i2c_set_clientdata(client, data); err = ds90uh925q_enable(client); if (err) goto err_out; return 0; err_out: i2c_set_clientdata(client, NULL); return err; } static int ds90uh925q_remove(struct i2c_client *client) { struct ds90uh925q_rec *data = i2c_get_clientdata(client); ds90uh925q_disable(client); i2c_set_clientdata(client, NULL); return 0; } static int ds90uh925q_suspend(struct i2c_client *client, pm_message_t message) { ds90uh925q_disable(client); return 0; } static int ds90uh925q_resume(struct i2c_client *client) { return ds90uh925q_enable(client); } static const struct i2c_device_id ds90uh925q_id[] = { { "ds90uh925q", 0 }, { } }; MODULE_DEVICE_TABLE(i2c, ds90uh925q_id); static struct i2c_driver ds90uh925q_driver = { .driver = { .name = "ds90uh925q", .owner = THIS_MODULE, }, .probe = ds90uh925q_probe, .remove = ds90uh925q_remove, .suspend = ds90uh925q_suspend, .resume = ds90uh925q_resume, .id_table = ds90uh925q_id, }; module_i2c_driver(ds90uh925q_driver); MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("DS90UH925Q FPDLink Serializer driver"); MODULE_ALIAS("i2c:ds90uh925q_ser");