linux/drivers/video/omap2/displays/panel-tfp410.c
Tomi Valkeinen 958f2717b8 OMAPDSS: TFP410: pdata rewrite
To ease device tree adaptation in the future, rewrite TFP410 platform
data handling to be done inside probe(), so that probe() is the only
place where we need to handle the DT/pdata choice.

Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
2012-05-11 14:44:50 +03:00

386 lines
7.6 KiB
C

/*
* TFP410 DPI-to-DVI chip
*
* Copyright (C) 2011 Texas Instruments Inc
* Author: Tomi Valkeinen <tomi.valkeinen@ti.com>
*
* 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.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/module.h>
#include <linux/slab.h>
#include <video/omapdss.h>
#include <linux/i2c.h>
#include <linux/gpio.h>
#include <drm/drm_edid.h>
#include <video/omap-panel-tfp410.h>
static const struct omap_video_timings tfp410_default_timings = {
.x_res = 640,
.y_res = 480,
.pixel_clock = 23500,
.hfp = 48,
.hsw = 32,
.hbp = 80,
.vfp = 3,
.vsw = 4,
.vbp = 7,
};
struct panel_drv_data {
struct omap_dss_device *dssdev;
struct mutex lock;
int pd_gpio;
struct i2c_adapter *i2c_adapter;
};
static int tfp410_power_on(struct omap_dss_device *dssdev)
{
struct panel_drv_data *ddata = dev_get_drvdata(&dssdev->dev);
int r;
if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE)
return 0;
r = omapdss_dpi_display_enable(dssdev);
if (r)
goto err0;
if (gpio_is_valid(ddata->pd_gpio))
gpio_set_value(ddata->pd_gpio, 1);
return 0;
err0:
return r;
}
static void tfp410_power_off(struct omap_dss_device *dssdev)
{
struct panel_drv_data *ddata = dev_get_drvdata(&dssdev->dev);
if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE)
return;
if (gpio_is_valid(ddata->pd_gpio))
gpio_set_value(ddata->pd_gpio, 0);
omapdss_dpi_display_disable(dssdev);
}
static int tfp410_probe(struct omap_dss_device *dssdev)
{
struct panel_drv_data *ddata;
int r;
int i2c_bus_num;
ddata = devm_kzalloc(&dssdev->dev, sizeof(*ddata), GFP_KERNEL);
if (!ddata)
return -ENOMEM;
dssdev->panel.timings = tfp410_default_timings;
dssdev->panel.config = OMAP_DSS_LCD_TFT;
ddata->dssdev = dssdev;
mutex_init(&ddata->lock);
if (dssdev->data) {
struct tfp410_platform_data *pdata = dssdev->data;
ddata->pd_gpio = pdata->power_down_gpio;
i2c_bus_num = pdata->i2c_bus_num;
} else {
ddata->pd_gpio = -1;
i2c_bus_num = -1;
}
if (gpio_is_valid(ddata->pd_gpio)) {
r = gpio_request_one(ddata->pd_gpio, GPIOF_OUT_INIT_LOW,
"tfp410 pd");
if (r) {
dev_err(&dssdev->dev, "Failed to request PD GPIO %d\n",
ddata->pd_gpio);
return r;
}
}
if (i2c_bus_num != -1) {
struct i2c_adapter *adapter;
adapter = i2c_get_adapter(i2c_bus_num);
if (!adapter) {
dev_err(&dssdev->dev, "Failed to get I2C adapter, bus %d\n",
i2c_bus_num);
r = -EINVAL;
goto err_i2c;
}
ddata->i2c_adapter = adapter;
}
dev_set_drvdata(&dssdev->dev, ddata);
return 0;
err_i2c:
if (gpio_is_valid(ddata->pd_gpio))
gpio_free(ddata->pd_gpio);
return r;
}
static void __exit tfp410_remove(struct omap_dss_device *dssdev)
{
struct panel_drv_data *ddata = dev_get_drvdata(&dssdev->dev);
mutex_lock(&ddata->lock);
if (ddata->i2c_adapter)
i2c_put_adapter(ddata->i2c_adapter);
if (gpio_is_valid(ddata->pd_gpio))
gpio_free(ddata->pd_gpio);
dev_set_drvdata(&dssdev->dev, NULL);
mutex_unlock(&ddata->lock);
}
static int tfp410_enable(struct omap_dss_device *dssdev)
{
struct panel_drv_data *ddata = dev_get_drvdata(&dssdev->dev);
int r;
mutex_lock(&ddata->lock);
r = tfp410_power_on(dssdev);
if (r == 0)
dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
mutex_unlock(&ddata->lock);
return r;
}
static void tfp410_disable(struct omap_dss_device *dssdev)
{
struct panel_drv_data *ddata = dev_get_drvdata(&dssdev->dev);
mutex_lock(&ddata->lock);
tfp410_power_off(dssdev);
dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
mutex_unlock(&ddata->lock);
}
static int tfp410_suspend(struct omap_dss_device *dssdev)
{
struct panel_drv_data *ddata = dev_get_drvdata(&dssdev->dev);
mutex_lock(&ddata->lock);
tfp410_power_off(dssdev);
dssdev->state = OMAP_DSS_DISPLAY_SUSPENDED;
mutex_unlock(&ddata->lock);
return 0;
}
static int tfp410_resume(struct omap_dss_device *dssdev)
{
struct panel_drv_data *ddata = dev_get_drvdata(&dssdev->dev);
int r;
mutex_lock(&ddata->lock);
r = tfp410_power_on(dssdev);
if (r == 0)
dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
mutex_unlock(&ddata->lock);
return r;
}
static void tfp410_set_timings(struct omap_dss_device *dssdev,
struct omap_video_timings *timings)
{
struct panel_drv_data *ddata = dev_get_drvdata(&dssdev->dev);
mutex_lock(&ddata->lock);
dpi_set_timings(dssdev, timings);
mutex_unlock(&ddata->lock);
}
static void tfp410_get_timings(struct omap_dss_device *dssdev,
struct omap_video_timings *timings)
{
struct panel_drv_data *ddata = dev_get_drvdata(&dssdev->dev);
mutex_lock(&ddata->lock);
*timings = dssdev->panel.timings;
mutex_unlock(&ddata->lock);
}
static int tfp410_check_timings(struct omap_dss_device *dssdev,
struct omap_video_timings *timings)
{
struct panel_drv_data *ddata = dev_get_drvdata(&dssdev->dev);
int r;
mutex_lock(&ddata->lock);
r = dpi_check_timings(dssdev, timings);
mutex_unlock(&ddata->lock);
return r;
}
static int tfp410_ddc_read(struct i2c_adapter *adapter,
unsigned char *buf, u16 count, u8 offset)
{
int r, retries;
for (retries = 3; retries > 0; retries--) {
struct i2c_msg msgs[] = {
{
.addr = DDC_ADDR,
.flags = 0,
.len = 1,
.buf = &offset,
}, {
.addr = DDC_ADDR,
.flags = I2C_M_RD,
.len = count,
.buf = buf,
}
};
r = i2c_transfer(adapter, msgs, 2);
if (r == 2)
return 0;
if (r != -EAGAIN)
break;
}
return r < 0 ? r : -EIO;
}
static int tfp410_read_edid(struct omap_dss_device *dssdev,
u8 *edid, int len)
{
struct panel_drv_data *ddata = dev_get_drvdata(&dssdev->dev);
int r, l, bytes_read;
mutex_lock(&ddata->lock);
if (!ddata->i2c_adapter) {
r = -ENODEV;
goto err;
}
l = min(EDID_LENGTH, len);
r = tfp410_ddc_read(ddata->i2c_adapter, edid, l, 0);
if (r)
goto err;
bytes_read = l;
/* if there are extensions, read second block */
if (len > EDID_LENGTH && edid[0x7e] > 0) {
l = min(EDID_LENGTH, len - EDID_LENGTH);
r = tfp410_ddc_read(ddata->i2c_adapter, edid + EDID_LENGTH,
l, EDID_LENGTH);
if (r)
goto err;
bytes_read += l;
}
mutex_unlock(&ddata->lock);
return bytes_read;
err:
mutex_unlock(&ddata->lock);
return r;
}
static bool tfp410_detect(struct omap_dss_device *dssdev)
{
struct panel_drv_data *ddata = dev_get_drvdata(&dssdev->dev);
unsigned char out;
int r;
mutex_lock(&ddata->lock);
if (!ddata->i2c_adapter)
goto out;
r = tfp410_ddc_read(ddata->i2c_adapter, &out, 1, 0);
mutex_unlock(&ddata->lock);
return r == 0;
out:
mutex_unlock(&ddata->lock);
return true;
}
static struct omap_dss_driver tfp410_driver = {
.probe = tfp410_probe,
.remove = __exit_p(tfp410_remove),
.enable = tfp410_enable,
.disable = tfp410_disable,
.suspend = tfp410_suspend,
.resume = tfp410_resume,
.set_timings = tfp410_set_timings,
.get_timings = tfp410_get_timings,
.check_timings = tfp410_check_timings,
.read_edid = tfp410_read_edid,
.detect = tfp410_detect,
.driver = {
.name = "tfp410",
.owner = THIS_MODULE,
},
};
static int __init tfp410_init(void)
{
return omap_dss_register_driver(&tfp410_driver);
}
static void __exit tfp410_exit(void)
{
omap_dss_unregister_driver(&tfp410_driver);
}
module_init(tfp410_init);
module_exit(tfp410_exit);
MODULE_LICENSE("GPL");