panel-tfp410.c 7.5 KB
Newer Older
1
/*
2
 * TFP410 DPI-to-DVI chip
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
 *
 * 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>
24
#include <linux/gpio.h>
25 26
#include <drm/drm_edid.h>

27
#include <video/omap-panel-tfp410.h>
28

29
static const struct omap_video_timings tfp410_default_timings = {
30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
	.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;
48 49

	int pd_gpio;
50

T
Tomi Valkeinen 已提交
51 52
	struct i2c_adapter *i2c_adapter;
};
53

54
static int tfp410_power_on(struct omap_dss_device *dssdev)
55
{
56
	struct panel_drv_data *ddata = dev_get_drvdata(&dssdev->dev);
57 58 59 60 61 62 63 64 65
	int r;

	if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE)
		return 0;

	r = omapdss_dpi_display_enable(dssdev);
	if (r)
		goto err0;

66
	if (gpio_is_valid(ddata->pd_gpio))
67
		gpio_set_value_cansleep(ddata->pd_gpio, 1);
68

69 70 71 72 73
	return 0;
err0:
	return r;
}

74
static void tfp410_power_off(struct omap_dss_device *dssdev)
75
{
76
	struct panel_drv_data *ddata = dev_get_drvdata(&dssdev->dev);
77 78 79 80

	if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE)
		return;

81
	if (gpio_is_valid(ddata->pd_gpio))
82
		gpio_set_value_cansleep(ddata->pd_gpio, 0);
83

84 85 86
	omapdss_dpi_display_disable(dssdev);
}

87
static int tfp410_probe(struct omap_dss_device *dssdev)
88 89
{
	struct panel_drv_data *ddata;
90
	int r;
T
Tomi Valkeinen 已提交
91
	int i2c_bus_num;
92

T
Tomi Valkeinen 已提交
93
	ddata = devm_kzalloc(&dssdev->dev, sizeof(*ddata), GFP_KERNEL);
94 95 96
	if (!ddata)
		return -ENOMEM;

97
	dssdev->panel.timings = tfp410_default_timings;
98 99 100 101

	ddata->dssdev = dssdev;
	mutex_init(&ddata->lock);

T
Tomi Valkeinen 已提交
102 103 104
	if (dssdev->data) {
		struct tfp410_platform_data *pdata = dssdev->data;

105
		ddata->pd_gpio = pdata->power_down_gpio;
T
Tomi Valkeinen 已提交
106 107
		i2c_bus_num = pdata->i2c_bus_num;
	} else {
108
		ddata->pd_gpio = -1;
T
Tomi Valkeinen 已提交
109 110
		i2c_bus_num = -1;
	}
111 112 113 114 115 116 117

	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);
T
Tomi Valkeinen 已提交
118
			return r;
119 120 121
		}
	}

T
Tomi Valkeinen 已提交
122 123 124 125 126 127 128 129 130 131 132 133 134 135
	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;
	}

136 137 138
	dev_set_drvdata(&dssdev->dev, ddata);

	return 0;
T
Tomi Valkeinen 已提交
139 140 141 142
err_i2c:
	if (gpio_is_valid(ddata->pd_gpio))
		gpio_free(ddata->pd_gpio);
	return r;
143 144
}

145
static void __exit tfp410_remove(struct omap_dss_device *dssdev)
146 147 148 149 150
{
	struct panel_drv_data *ddata = dev_get_drvdata(&dssdev->dev);

	mutex_lock(&ddata->lock);

T
Tomi Valkeinen 已提交
151 152 153
	if (ddata->i2c_adapter)
		i2c_put_adapter(ddata->i2c_adapter);

154 155 156
	if (gpio_is_valid(ddata->pd_gpio))
		gpio_free(ddata->pd_gpio);

157 158 159 160 161
	dev_set_drvdata(&dssdev->dev, NULL);

	mutex_unlock(&ddata->lock);
}

162
static int tfp410_enable(struct omap_dss_device *dssdev)
163 164 165 166 167 168
{
	struct panel_drv_data *ddata = dev_get_drvdata(&dssdev->dev);
	int r;

	mutex_lock(&ddata->lock);

169
	r = tfp410_power_on(dssdev);
170 171 172 173 174 175 176 177
	if (r == 0)
		dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;

	mutex_unlock(&ddata->lock);

	return r;
}

178
static void tfp410_disable(struct omap_dss_device *dssdev)
179 180 181 182 183
{
	struct panel_drv_data *ddata = dev_get_drvdata(&dssdev->dev);

	mutex_lock(&ddata->lock);

184
	tfp410_power_off(dssdev);
185 186 187 188 189 190

	dssdev->state = OMAP_DSS_DISPLAY_DISABLED;

	mutex_unlock(&ddata->lock);
}

191
static int tfp410_suspend(struct omap_dss_device *dssdev)
192 193 194 195 196
{
	struct panel_drv_data *ddata = dev_get_drvdata(&dssdev->dev);

	mutex_lock(&ddata->lock);

197
	tfp410_power_off(dssdev);
198 199 200 201 202 203 204 205

	dssdev->state = OMAP_DSS_DISPLAY_SUSPENDED;

	mutex_unlock(&ddata->lock);

	return 0;
}

206
static int tfp410_resume(struct omap_dss_device *dssdev)
207 208 209 210 211 212
{
	struct panel_drv_data *ddata = dev_get_drvdata(&dssdev->dev);
	int r;

	mutex_lock(&ddata->lock);

213
	r = tfp410_power_on(dssdev);
214 215 216 217 218 219 220 221
	if (r == 0)
		dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;

	mutex_unlock(&ddata->lock);

	return r;
}

222
static void tfp410_set_timings(struct omap_dss_device *dssdev,
223 224 225 226 227 228 229 230 231
		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);
}

232
static void tfp410_get_timings(struct omap_dss_device *dssdev,
233 234 235 236 237 238 239 240 241
		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);
}

242
static int tfp410_check_timings(struct omap_dss_device *dssdev,
243 244 245 246 247 248 249 250 251 252 253 254 255
		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;
}


256
static int tfp410_ddc_read(struct i2c_adapter *adapter,
257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286
		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;
}

287
static int tfp410_read_edid(struct omap_dss_device *dssdev,
288 289 290 291 292 293 294
		u8 *edid, int len)
{
	struct panel_drv_data *ddata = dev_get_drvdata(&dssdev->dev);
	int r, l, bytes_read;

	mutex_lock(&ddata->lock);

T
Tomi Valkeinen 已提交
295
	if (!ddata->i2c_adapter) {
296 297 298 299 300
		r = -ENODEV;
		goto err;
	}

	l = min(EDID_LENGTH, len);
T
Tomi Valkeinen 已提交
301
	r = tfp410_ddc_read(ddata->i2c_adapter, edid, l, 0);
302 303 304 305 306 307 308 309 310
	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);

T
Tomi Valkeinen 已提交
311
		r = tfp410_ddc_read(ddata->i2c_adapter, edid + EDID_LENGTH,
312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327
				l, EDID_LENGTH);
		if (r)
			goto err;

		bytes_read += l;
	}

	mutex_unlock(&ddata->lock);

	return bytes_read;

err:
	mutex_unlock(&ddata->lock);
	return r;
}

328
static bool tfp410_detect(struct omap_dss_device *dssdev)
329 330 331 332 333 334 335
{
	struct panel_drv_data *ddata = dev_get_drvdata(&dssdev->dev);
	unsigned char out;
	int r;

	mutex_lock(&ddata->lock);

T
Tomi Valkeinen 已提交
336
	if (!ddata->i2c_adapter)
337 338
		goto out;

T
Tomi Valkeinen 已提交
339
	r = tfp410_ddc_read(ddata->i2c_adapter, &out, 1, 0);
340 341 342 343 344 345 346 347 348 349

	mutex_unlock(&ddata->lock);

	return r == 0;

out:
	mutex_unlock(&ddata->lock);
	return true;
}

350 351 352
static struct omap_dss_driver tfp410_driver = {
	.probe		= tfp410_probe,
	.remove		= __exit_p(tfp410_remove),
353

354 355 356 357
	.enable		= tfp410_enable,
	.disable	= tfp410_disable,
	.suspend	= tfp410_suspend,
	.resume		= tfp410_resume,
358

359 360 361
	.set_timings	= tfp410_set_timings,
	.get_timings	= tfp410_get_timings,
	.check_timings	= tfp410_check_timings,
362

363 364
	.read_edid	= tfp410_read_edid,
	.detect		= tfp410_detect,
365 366

	.driver         = {
367
		.name   = "tfp410",
368 369 370 371
		.owner  = THIS_MODULE,
	},
};

372
static int __init tfp410_init(void)
373
{
374
	return omap_dss_register_driver(&tfp410_driver);
375 376
}

377
static void __exit tfp410_exit(void)
378
{
379
	omap_dss_unregister_driver(&tfp410_driver);
380 381
}

382 383
module_init(tfp410_init);
module_exit(tfp410_exit);
384
MODULE_LICENSE("GPL");