1c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben Dooks/* drivers/video/backlight/platform_lcd.c 2c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben Dooks * 3c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben Dooks * Copyright 2008 Simtec Electronics 4c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben Dooks * Ben Dooks <ben@simtec.co.uk> 5c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben Dooks * 6c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben Dooks * Generic platform-device LCD power control interface. 7c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben Dooks * 8c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben Dooks * This program is free software; you can redistribute it and/or modify 9c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben Dooks * it under the terms of the GNU General Public License version 2 as 10c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben Dooks * published by the Free Software Foundation. 11c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben Dooks * 12c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben Dooks*/ 13c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben Dooks 14c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben Dooks#include <linux/module.h> 15c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben Dooks#include <linux/platform_device.h> 16c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben Dooks#include <linux/fb.h> 17c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben Dooks#include <linux/backlight.h> 18c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben Dooks#include <linux/lcd.h> 195a0e3ad6af8660be21ca98a971cd00f331318c05Tejun Heo#include <linux/slab.h> 20c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben Dooks 21c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben Dooks#include <video/platform_lcd.h> 22c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben Dooks 23c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben Dooksstruct platform_lcd { 24c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben Dooks struct device *us; 25c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben Dooks struct lcd_device *lcd; 26c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben Dooks struct plat_lcd_data *pdata; 27c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben Dooks 28c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben Dooks unsigned int power; 29c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben Dooks unsigned int suspended : 1; 30c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben Dooks}; 31c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben Dooks 32c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben Dooksstatic inline struct platform_lcd *to_our_lcd(struct lcd_device *lcd) 33c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben Dooks{ 34c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben Dooks return lcd_get_data(lcd); 35c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben Dooks} 36c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben Dooks 37c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben Dooksstatic int platform_lcd_get_power(struct lcd_device *lcd) 38c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben Dooks{ 39c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben Dooks struct platform_lcd *plcd = to_our_lcd(lcd); 40c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben Dooks 41c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben Dooks return plcd->power; 42c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben Dooks} 43c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben Dooks 44c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben Dooksstatic int platform_lcd_set_power(struct lcd_device *lcd, int power) 45c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben Dooks{ 46c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben Dooks struct platform_lcd *plcd = to_our_lcd(lcd); 47c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben Dooks int lcd_power = 1; 48c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben Dooks 49c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben Dooks if (power == FB_BLANK_POWERDOWN || plcd->suspended) 50c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben Dooks lcd_power = 0; 51c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben Dooks 52c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben Dooks plcd->pdata->set_power(plcd->pdata, lcd_power); 53c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben Dooks plcd->power = power; 54c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben Dooks 55c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben Dooks return 0; 56c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben Dooks} 57c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben Dooks 58c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben Dooksstatic int platform_lcd_match(struct lcd_device *lcd, struct fb_info *info) 59c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben Dooks{ 60c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben Dooks struct platform_lcd *plcd = to_our_lcd(lcd); 61c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben Dooks struct plat_lcd_data *pdata = plcd->pdata; 62c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben Dooks 63c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben Dooks if (pdata->match_fb) 64c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben Dooks return pdata->match_fb(pdata, info); 65c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben Dooks 66c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben Dooks return plcd->us->parent == info->device; 67c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben Dooks} 68c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben Dooks 69c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben Dooksstatic struct lcd_ops platform_lcd_ops = { 70c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben Dooks .get_power = platform_lcd_get_power, 71c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben Dooks .set_power = platform_lcd_set_power, 72c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben Dooks .check_fb = platform_lcd_match, 73c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben Dooks}; 74c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben Dooks 75c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben Dooksstatic int __devinit platform_lcd_probe(struct platform_device *pdev) 76c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben Dooks{ 77c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben Dooks struct plat_lcd_data *pdata; 78c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben Dooks struct platform_lcd *plcd; 79c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben Dooks struct device *dev = &pdev->dev; 80c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben Dooks int err; 81c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben Dooks 82c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben Dooks pdata = pdev->dev.platform_data; 83c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben Dooks if (!pdata) { 84c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben Dooks dev_err(dev, "no platform data supplied\n"); 85c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben Dooks return -EINVAL; 86c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben Dooks } 87c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben Dooks 8848e78e8cc87ab80617ef0c5a146701ca96a4a51dMark Brown plcd = devm_kzalloc(&pdev->dev, sizeof(struct platform_lcd), 8948e78e8cc87ab80617ef0c5a146701ca96a4a51dMark Brown GFP_KERNEL); 90c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben Dooks if (!plcd) { 91c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben Dooks dev_err(dev, "no memory for state\n"); 92c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben Dooks return -ENOMEM; 93c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben Dooks } 94c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben Dooks 95c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben Dooks plcd->us = dev; 96c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben Dooks plcd->pdata = pdata; 97e958d3ace7791f33518f0259cd3cf229408b135cBen Dooks plcd->lcd = lcd_device_register(dev_name(dev), dev, 98c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben Dooks plcd, &platform_lcd_ops); 99c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben Dooks if (IS_ERR(plcd->lcd)) { 100c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben Dooks dev_err(dev, "cannot register lcd device\n"); 101c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben Dooks err = PTR_ERR(plcd->lcd); 10248e78e8cc87ab80617ef0c5a146701ca96a4a51dMark Brown goto err; 103c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben Dooks } 104c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben Dooks 105c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben Dooks platform_set_drvdata(pdev, plcd); 106126ed36d0edee41c0775906a164ad7e8bef55864Ben Dooks platform_lcd_set_power(plcd->lcd, FB_BLANK_NORMAL); 107126ed36d0edee41c0775906a164ad7e8bef55864Ben Dooks 108c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben Dooks return 0; 109c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben Dooks 11048e78e8cc87ab80617ef0c5a146701ca96a4a51dMark Brown err: 111c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben Dooks return err; 112c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben Dooks} 113c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben Dooks 114c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben Dooksstatic int __devexit platform_lcd_remove(struct platform_device *pdev) 115c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben Dooks{ 116c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben Dooks struct platform_lcd *plcd = platform_get_drvdata(pdev); 117c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben Dooks 118c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben Dooks lcd_device_unregister(plcd->lcd); 119c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben Dooks 120c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben Dooks return 0; 121c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben Dooks} 122c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben Dooks 123c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben Dooks#ifdef CONFIG_PM 12467a67272e890c79372bc0e2e555071f903d864a7Jingoo Hanstatic int platform_lcd_suspend(struct device *dev) 125c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben Dooks{ 12667a67272e890c79372bc0e2e555071f903d864a7Jingoo Han struct platform_lcd *plcd = dev_get_drvdata(dev); 127c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben Dooks 128c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben Dooks plcd->suspended = 1; 129c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben Dooks platform_lcd_set_power(plcd->lcd, plcd->power); 130c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben Dooks 131c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben Dooks return 0; 132c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben Dooks} 133c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben Dooks 13467a67272e890c79372bc0e2e555071f903d864a7Jingoo Hanstatic int platform_lcd_resume(struct device *dev) 135c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben Dooks{ 13667a67272e890c79372bc0e2e555071f903d864a7Jingoo Han struct platform_lcd *plcd = dev_get_drvdata(dev); 137c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben Dooks 138c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben Dooks plcd->suspended = 0; 139c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben Dooks platform_lcd_set_power(plcd->lcd, plcd->power); 140c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben Dooks 141c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben Dooks return 0; 142c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben Dooks} 14367a67272e890c79372bc0e2e555071f903d864a7Jingoo Han 14467a67272e890c79372bc0e2e555071f903d864a7Jingoo Hanstatic SIMPLE_DEV_PM_OPS(platform_lcd_pm_ops, platform_lcd_suspend, 14567a67272e890c79372bc0e2e555071f903d864a7Jingoo Han platform_lcd_resume); 146c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben Dooks#endif 147c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben Dooks 148c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben Dooksstatic struct platform_driver platform_lcd_driver = { 149c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben Dooks .driver = { 150c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben Dooks .name = "platform-lcd", 151c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben Dooks .owner = THIS_MODULE, 15267a67272e890c79372bc0e2e555071f903d864a7Jingoo Han#ifdef CONFIG_PM 15367a67272e890c79372bc0e2e555071f903d864a7Jingoo Han .pm = &platform_lcd_pm_ops, 15467a67272e890c79372bc0e2e555071f903d864a7Jingoo Han#endif 155c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben Dooks }, 156c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben Dooks .probe = platform_lcd_probe, 157c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben Dooks .remove = __devexit_p(platform_lcd_remove), 158c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben Dooks}; 159c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben Dooks 16081178e021689bf86c328f144aa0f0e1b50f5e94cAxel Linmodule_platform_driver(platform_lcd_driver); 161c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben Dooks 162c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben DooksMODULE_AUTHOR("Ben Dooks <ben-linux@fluff.org>"); 163c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben DooksMODULE_LICENSE("GPL v2"); 164c25826a7cf1c61b5c6e6db8365172eb97ef39ef3Ben DooksMODULE_ALIAS("platform:platform-lcd"); 165