|
@@ -53,10 +53,16 @@ struct dcon_platform_data {
|
|
|
|
|
|
static struct dcon_platform_data *pdata;
|
|
|
|
|
|
+struct dcon_priv {
|
|
|
+ struct i2c_client *client;
|
|
|
+
|
|
|
+ struct work_struct switch_source;
|
|
|
+ struct notifier_block reboot_nb;
|
|
|
+};
|
|
|
+
|
|
|
/* I2C structures */
|
|
|
|
|
|
static struct i2c_driver dcon_driver;
|
|
|
-static struct i2c_client *dcon_client;
|
|
|
|
|
|
/* Platform devices */
|
|
|
static struct platform_device *dcon_device;
|
|
@@ -93,16 +99,24 @@ static DECLARE_WAIT_QUEUE_HEAD(dcon_wait_queue);
|
|
|
|
|
|
static unsigned short normal_i2c[] = { 0x0d, I2C_CLIENT_END };
|
|
|
|
|
|
-#define dcon_write(reg, val) i2c_smbus_write_word_data(dcon_client, reg, val)
|
|
|
-#define dcon_read(reg) i2c_smbus_read_word_data(dcon_client, reg)
|
|
|
+static s32 dcon_write(struct dcon_priv *dcon, u8 reg, u16 val)
|
|
|
+{
|
|
|
+ return i2c_smbus_write_word_data(dcon->client, reg, val);
|
|
|
+}
|
|
|
+
|
|
|
+static s32 dcon_read(struct dcon_priv *dcon, u8 reg)
|
|
|
+{
|
|
|
+ return i2c_smbus_read_word_data(dcon->client, reg);
|
|
|
+}
|
|
|
|
|
|
/* The current backlight value - this saves us some smbus traffic */
|
|
|
static int bl_val = -1;
|
|
|
|
|
|
/* ===== API functions - these are called by a variety of users ==== */
|
|
|
|
|
|
-static int dcon_hw_init(struct i2c_client *client, int is_init)
|
|
|
+static int dcon_hw_init(struct dcon_priv *dcon, int is_init)
|
|
|
{
|
|
|
+ struct i2c_client *client = dcon->client;
|
|
|
uint16_t ver;
|
|
|
int rc = 0;
|
|
|
|
|
@@ -173,7 +187,7 @@ err:
|
|
|
* smbus. For newer models, we simply BUG(); we want to know if this
|
|
|
* still happens despite the power fixes that have been made!
|
|
|
*/
|
|
|
-static int dcon_bus_stabilize(struct i2c_client *client, int is_powered_down)
|
|
|
+static int dcon_bus_stabilize(struct dcon_priv *dcon, int is_powered_down)
|
|
|
{
|
|
|
unsigned long timeout;
|
|
|
int x;
|
|
@@ -194,7 +208,7 @@ power_up:
|
|
|
|
|
|
for (x = -1, timeout = 50; timeout && x < 0; timeout--) {
|
|
|
msleep(1);
|
|
|
- x = dcon_read(DCON_REG_ID);
|
|
|
+ x = dcon_read(dcon, DCON_REG_ID);
|
|
|
}
|
|
|
if (x < 0) {
|
|
|
printk(KERN_ERR "olpc-dcon: unable to stabilize dcon's "
|
|
@@ -208,51 +222,51 @@ power_up:
|
|
|
}
|
|
|
|
|
|
if (is_powered_down)
|
|
|
- return dcon_hw_init(client, 0);
|
|
|
+ return dcon_hw_init(dcon, 0);
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
-static int dcon_get_backlight(void)
|
|
|
+static int dcon_get_backlight(struct dcon_priv *dcon)
|
|
|
{
|
|
|
- if (dcon_client == NULL)
|
|
|
+ if (!dcon || !dcon->client)
|
|
|
return 0;
|
|
|
|
|
|
if (bl_val == -1)
|
|
|
- bl_val = dcon_read(DCON_REG_BRIGHT) & 0x0F;
|
|
|
+ bl_val = dcon_read(dcon, DCON_REG_BRIGHT) & 0x0F;
|
|
|
|
|
|
return bl_val;
|
|
|
}
|
|
|
|
|
|
|
|
|
-static void dcon_set_backlight_hw(int level)
|
|
|
+static void dcon_set_backlight_hw(struct dcon_priv *dcon, int level)
|
|
|
{
|
|
|
bl_val = level & 0x0F;
|
|
|
- dcon_write(DCON_REG_BRIGHT, bl_val);
|
|
|
+ dcon_write(dcon, DCON_REG_BRIGHT, bl_val);
|
|
|
|
|
|
/* Purposely turn off the backlight when we go to level 0 */
|
|
|
if (bl_val == 0) {
|
|
|
dcon_disp_mode &= ~MODE_BL_ENABLE;
|
|
|
- dcon_write(DCON_REG_MODE, dcon_disp_mode);
|
|
|
+ dcon_write(dcon, DCON_REG_MODE, dcon_disp_mode);
|
|
|
} else if (!(dcon_disp_mode & MODE_BL_ENABLE)) {
|
|
|
dcon_disp_mode |= MODE_BL_ENABLE;
|
|
|
- dcon_write(DCON_REG_MODE, dcon_disp_mode);
|
|
|
+ dcon_write(dcon, DCON_REG_MODE, dcon_disp_mode);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-static void dcon_set_backlight(int level)
|
|
|
+static void dcon_set_backlight(struct dcon_priv *dcon, int level)
|
|
|
{
|
|
|
- if (dcon_client == NULL)
|
|
|
+ if (!dcon || !dcon->client)
|
|
|
return;
|
|
|
|
|
|
if (bl_val == (level & 0x0F))
|
|
|
return;
|
|
|
|
|
|
- dcon_set_backlight_hw(level);
|
|
|
+ dcon_set_backlight_hw(dcon, level);
|
|
|
}
|
|
|
|
|
|
/* Set the output type to either color or mono */
|
|
|
|
|
|
-static int dcon_set_output(int arg)
|
|
|
+static int dcon_set_output(struct dcon_priv *dcon, int arg)
|
|
|
{
|
|
|
if (dcon_output == arg)
|
|
|
return 0;
|
|
@@ -269,7 +283,7 @@ static int dcon_set_output(int arg)
|
|
|
dcon_disp_mode |= MODE_COL_AA;
|
|
|
}
|
|
|
|
|
|
- dcon_write(DCON_REG_MODE, dcon_disp_mode);
|
|
|
+ dcon_write(dcon, DCON_REG_MODE, dcon_disp_mode);
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
@@ -277,7 +291,7 @@ static int dcon_set_output(int arg)
|
|
|
* DCONLOAD works in a sleep and account for it accordingly
|
|
|
*/
|
|
|
|
|
|
-static void dcon_sleep(int state)
|
|
|
+static void dcon_sleep(struct dcon_priv *dcon, int state)
|
|
|
{
|
|
|
int x;
|
|
|
|
|
@@ -301,7 +315,7 @@ static void dcon_sleep(int state)
|
|
|
/* Only re-enable the backlight if the backlight value is set */
|
|
|
if (bl_val != 0)
|
|
|
dcon_disp_mode |= MODE_BL_ENABLE;
|
|
|
- x = dcon_bus_stabilize(dcon_client, 1);
|
|
|
+ x = dcon_bus_stabilize(dcon, 1);
|
|
|
if (x)
|
|
|
printk(KERN_WARNING "olpc-dcon: unable to reinit dcon"
|
|
|
" hardware: %d!\n", x);
|
|
@@ -309,7 +323,7 @@ static void dcon_sleep(int state)
|
|
|
dcon_sleep_val = state;
|
|
|
|
|
|
/* Restore backlight */
|
|
|
- dcon_set_backlight_hw(bl_val);
|
|
|
+ dcon_set_backlight_hw(dcon, bl_val);
|
|
|
}
|
|
|
|
|
|
/* We should turn off some stuff in the framebuffer - but what? */
|
|
@@ -337,6 +351,8 @@ void dcon_load_holdoff(void)
|
|
|
|
|
|
static void dcon_source_switch(struct work_struct *work)
|
|
|
{
|
|
|
+ struct dcon_priv *dcon = container_of(work, struct dcon_priv,
|
|
|
+ switch_source);
|
|
|
DECLARE_WAITQUEUE(wait, current);
|
|
|
int source = dcon_pending;
|
|
|
|
|
@@ -351,7 +367,8 @@ static void dcon_source_switch(struct work_struct *work)
|
|
|
case DCON_SOURCE_CPU:
|
|
|
printk("dcon_source_switch to CPU\n");
|
|
|
/* Enable the scanline interrupt bit */
|
|
|
- if (dcon_write(DCON_REG_MODE, dcon_disp_mode | MODE_SCAN_INT))
|
|
|
+ if (dcon_write(dcon, DCON_REG_MODE,
|
|
|
+ dcon_disp_mode | MODE_SCAN_INT))
|
|
|
printk(KERN_ERR
|
|
|
"olpc-dcon: couldn't enable scanline interrupt!\n");
|
|
|
else {
|
|
@@ -364,7 +381,7 @@ static void dcon_source_switch(struct work_struct *work)
|
|
|
printk(KERN_ERR "olpc-dcon: Timeout entering CPU mode; expect a screen glitch.\n");
|
|
|
|
|
|
/* Turn off the scanline interrupt */
|
|
|
- if (dcon_write(DCON_REG_MODE, dcon_disp_mode))
|
|
|
+ if (dcon_write(dcon, DCON_REG_MODE, dcon_disp_mode))
|
|
|
printk(KERN_ERR "olpc-dcon: couldn't disable scanline interrupt!\n");
|
|
|
|
|
|
/*
|
|
@@ -454,40 +471,39 @@ static void dcon_source_switch(struct work_struct *work)
|
|
|
dcon_source = source;
|
|
|
}
|
|
|
|
|
|
-static DECLARE_WORK(dcon_work, dcon_source_switch);
|
|
|
-
|
|
|
-static void dcon_set_source(int arg)
|
|
|
+static void dcon_set_source(struct dcon_priv *dcon, int arg)
|
|
|
{
|
|
|
if (dcon_pending == arg)
|
|
|
return;
|
|
|
|
|
|
dcon_pending = arg;
|
|
|
|
|
|
- if ((dcon_source != arg) && !work_pending(&dcon_work))
|
|
|
- schedule_work(&dcon_work);
|
|
|
+ if ((dcon_source != arg) && !work_pending(&dcon->switch_source))
|
|
|
+ schedule_work(&dcon->switch_source);
|
|
|
}
|
|
|
|
|
|
-static void dcon_set_source_sync(int arg)
|
|
|
+static void dcon_set_source_sync(struct dcon_priv *dcon, int arg)
|
|
|
{
|
|
|
- dcon_set_source(arg);
|
|
|
+ dcon_set_source(dcon, arg);
|
|
|
flush_scheduled_work();
|
|
|
}
|
|
|
|
|
|
static int dconbl_set(struct backlight_device *dev)
|
|
|
{
|
|
|
-
|
|
|
+ struct dcon_priv *dcon = bl_get_data(dev);
|
|
|
int level = dev->props.brightness;
|
|
|
|
|
|
if (dev->props.power != FB_BLANK_UNBLANK)
|
|
|
level = 0;
|
|
|
|
|
|
- dcon_set_backlight(level);
|
|
|
+ dcon_set_backlight(dcon, level);
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
static int dconbl_get(struct backlight_device *dev)
|
|
|
{
|
|
|
- return dcon_get_backlight();
|
|
|
+ struct dcon_priv *dcon = bl_get_data(dev);
|
|
|
+ return dcon_get_backlight(dcon);
|
|
|
}
|
|
|
|
|
|
static ssize_t dcon_mode_show(struct device *dev,
|
|
@@ -548,7 +564,7 @@ static ssize_t dcon_output_store(struct device *dev,
|
|
|
return -EINVAL;
|
|
|
|
|
|
if (output == DCON_OUTPUT_COLOR || output == DCON_OUTPUT_MONO) {
|
|
|
- dcon_set_output(output);
|
|
|
+ dcon_set_output(dev_get_drvdata(dev), output);
|
|
|
rc = count;
|
|
|
}
|
|
|
|
|
@@ -558,6 +574,7 @@ static ssize_t dcon_output_store(struct device *dev,
|
|
|
static ssize_t dcon_freeze_store(struct device *dev,
|
|
|
struct device_attribute *attr, const char *buf, size_t count)
|
|
|
{
|
|
|
+ struct dcon_priv *dcon = dev_get_drvdata(dev);
|
|
|
int output;
|
|
|
|
|
|
if (_strtoul(buf, count, &output))
|
|
@@ -567,13 +584,13 @@ static ssize_t dcon_freeze_store(struct device *dev,
|
|
|
|
|
|
switch (output) {
|
|
|
case 0:
|
|
|
- dcon_set_source(DCON_SOURCE_CPU);
|
|
|
+ dcon_set_source(dcon, DCON_SOURCE_CPU);
|
|
|
break;
|
|
|
case 1:
|
|
|
- dcon_set_source_sync(DCON_SOURCE_DCON);
|
|
|
+ dcon_set_source_sync(dcon, DCON_SOURCE_DCON);
|
|
|
break;
|
|
|
case 2: /* normally unused */
|
|
|
- dcon_set_source(DCON_SOURCE_DCON);
|
|
|
+ dcon_set_source(dcon, DCON_SOURCE_DCON);
|
|
|
break;
|
|
|
default:
|
|
|
return -EINVAL;
|
|
@@ -592,7 +609,7 @@ static ssize_t dcon_resumeline_store(struct device *dev,
|
|
|
return rc;
|
|
|
|
|
|
resumeline = rl;
|
|
|
- dcon_write(DCON_REG_SCAN_INT, resumeline);
|
|
|
+ dcon_write(dev_get_drvdata(dev), DCON_REG_SCAN_INT, resumeline);
|
|
|
rc = count;
|
|
|
|
|
|
return rc;
|
|
@@ -606,7 +623,7 @@ static ssize_t dcon_sleep_store(struct device *dev,
|
|
|
if (_strtoul(buf, count, &output))
|
|
|
return -EINVAL;
|
|
|
|
|
|
- dcon_sleep(output ? DCON_SLEEP : DCON_ACTIVE);
|
|
|
+ dcon_sleep(dev_get_drvdata(dev), output ? DCON_SLEEP : DCON_ACTIVE);
|
|
|
return count;
|
|
|
}
|
|
|
|
|
@@ -627,20 +644,17 @@ static const struct backlight_ops dcon_bl_ops = {
|
|
|
static int dcon_reboot_notify(struct notifier_block *nb,
|
|
|
unsigned long foo, void *bar)
|
|
|
{
|
|
|
- if (dcon_client == NULL)
|
|
|
+ struct dcon_priv *dcon = container_of(nb, struct dcon_priv, reboot_nb);
|
|
|
+
|
|
|
+ if (!dcon || !dcon->client)
|
|
|
return 0;
|
|
|
|
|
|
/* Turn off the DCON. Entirely. */
|
|
|
- dcon_write(DCON_REG_MODE, 0x39);
|
|
|
- dcon_write(DCON_REG_MODE, 0x32);
|
|
|
+ dcon_write(dcon, DCON_REG_MODE, 0x39);
|
|
|
+ dcon_write(dcon, DCON_REG_MODE, 0x32);
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
-static struct notifier_block dcon_nb = {
|
|
|
- .notifier_call = dcon_reboot_notify,
|
|
|
- .priority = -1,
|
|
|
-};
|
|
|
-
|
|
|
static int unfreeze_on_panic(struct notifier_block *nb,
|
|
|
unsigned long e, void *p)
|
|
|
{
|
|
@@ -660,11 +674,14 @@ static int fb_notifier_callback(struct notifier_block *self,
|
|
|
unsigned long event, void *data)
|
|
|
{
|
|
|
struct fb_event *evdata = data;
|
|
|
+ struct backlight_device *bl = container_of(self,
|
|
|
+ struct backlight_device, fb_notif);
|
|
|
+ struct dcon_priv *dcon = bl_get_data(bl);
|
|
|
int *blank = (int *) evdata->data;
|
|
|
if (((event != FB_EVENT_BLANK) && (event != FB_EVENT_CONBLANK)) ||
|
|
|
ignore_fb_events)
|
|
|
return 0;
|
|
|
- dcon_sleep((*blank) ? DCON_SLEEP : DCON_ACTIVE);
|
|
|
+ dcon_sleep(dcon, (*blank) ? DCON_SLEEP : DCON_ACTIVE);
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
@@ -681,12 +698,24 @@ static int dcon_detect(struct i2c_client *client, struct i2c_board_info *info)
|
|
|
|
|
|
static int dcon_probe(struct i2c_client *client, const struct i2c_device_id *id)
|
|
|
{
|
|
|
+ struct dcon_priv *dcon;
|
|
|
int rc, i, j;
|
|
|
|
|
|
+ dcon = kzalloc(sizeof(*dcon), GFP_KERNEL);
|
|
|
+ if (!dcon)
|
|
|
+ return -ENOMEM;
|
|
|
+
|
|
|
+ dcon->client = client;
|
|
|
+ INIT_WORK(&dcon->switch_source, dcon_source_switch);
|
|
|
+ dcon->reboot_nb.notifier_call = dcon_reboot_notify;
|
|
|
+ dcon->reboot_nb.priority = -1;
|
|
|
+
|
|
|
+ i2c_set_clientdata(client, dcon);
|
|
|
+
|
|
|
if (num_registered_fb >= 1)
|
|
|
fbinfo = registered_fb[0];
|
|
|
|
|
|
- rc = dcon_hw_init(client, 1);
|
|
|
+ rc = dcon_hw_init(dcon, 1);
|
|
|
if (rc)
|
|
|
goto einit;
|
|
|
|
|
@@ -699,9 +728,8 @@ static int dcon_probe(struct i2c_client *client, const struct i2c_device_id *id)
|
|
|
rc = -ENOMEM;
|
|
|
goto eirq;
|
|
|
}
|
|
|
- /* Place holder...*/
|
|
|
- i2c_set_clientdata(client, dcon_device);
|
|
|
rc = platform_device_add(dcon_device);
|
|
|
+ platform_set_drvdata(dcon_device, dcon);
|
|
|
|
|
|
if (rc) {
|
|
|
printk(KERN_ERR "dcon: Unable to add the DCON device\n");
|
|
@@ -718,11 +746,8 @@ static int dcon_probe(struct i2c_client *client, const struct i2c_device_id *id)
|
|
|
}
|
|
|
|
|
|
/* Add the backlight device for the DCON */
|
|
|
-
|
|
|
- dcon_client = client;
|
|
|
-
|
|
|
dcon_bl_dev = backlight_device_register("dcon-bl", &dcon_device->dev,
|
|
|
- NULL, &dcon_bl_ops, NULL);
|
|
|
+ dcon, &dcon_bl_ops, NULL);
|
|
|
|
|
|
if (IS_ERR(dcon_bl_dev)) {
|
|
|
printk(KERN_ERR "Cannot register the backlight device (%ld)\n",
|
|
@@ -731,12 +756,12 @@ static int dcon_probe(struct i2c_client *client, const struct i2c_device_id *id)
|
|
|
} else {
|
|
|
dcon_bl_dev->props.max_brightness = 15;
|
|
|
dcon_bl_dev->props.power = FB_BLANK_UNBLANK;
|
|
|
- dcon_bl_dev->props.brightness = dcon_get_backlight();
|
|
|
+ dcon_bl_dev->props.brightness = dcon_get_backlight(dcon);
|
|
|
|
|
|
backlight_update_status(dcon_bl_dev);
|
|
|
}
|
|
|
|
|
|
- register_reboot_notifier(&dcon_nb);
|
|
|
+ register_reboot_notifier(&dcon->reboot_nb);
|
|
|
atomic_notifier_chain_register(&panic_notifier_list, &dcon_panic_nb);
|
|
|
fb_register_client(&fb_nb);
|
|
|
|
|
@@ -751,15 +776,19 @@ static int dcon_probe(struct i2c_client *client, const struct i2c_device_id *id)
|
|
|
eirq:
|
|
|
free_irq(DCON_IRQ, &dcon_driver);
|
|
|
einit:
|
|
|
+ i2c_set_clientdata(client, NULL);
|
|
|
+ kfree(dcon);
|
|
|
return rc;
|
|
|
}
|
|
|
|
|
|
static int dcon_remove(struct i2c_client *client)
|
|
|
{
|
|
|
- dcon_client = NULL;
|
|
|
+ struct dcon_priv *dcon = i2c_get_clientdata(client);
|
|
|
+
|
|
|
+ i2c_set_clientdata(client, NULL);
|
|
|
|
|
|
fb_unregister_client(&fb_nb);
|
|
|
- unregister_reboot_notifier(&dcon_nb);
|
|
|
+ unregister_reboot_notifier(&dcon->reboot_nb);
|
|
|
atomic_notifier_chain_unregister(&panic_notifier_list, &dcon_panic_nb);
|
|
|
|
|
|
free_irq(DCON_IRQ, &dcon_driver);
|
|
@@ -769,7 +798,9 @@ static int dcon_remove(struct i2c_client *client)
|
|
|
|
|
|
if (dcon_device != NULL)
|
|
|
platform_device_unregister(dcon_device);
|
|
|
- cancel_work_sync(&dcon_work);
|
|
|
+ cancel_work_sync(&dcon->switch_source);
|
|
|
+
|
|
|
+ kfree(dcon);
|
|
|
|
|
|
return 0;
|
|
|
}
|
|
@@ -777,9 +808,11 @@ static int dcon_remove(struct i2c_client *client)
|
|
|
#ifdef CONFIG_PM
|
|
|
static int dcon_suspend(struct i2c_client *client, pm_message_t state)
|
|
|
{
|
|
|
+ struct dcon_priv *dcon = i2c_get_clientdata(client);
|
|
|
+
|
|
|
if (dcon_sleep_val == DCON_ACTIVE) {
|
|
|
/* Set up the DCON to have the source */
|
|
|
- dcon_set_source_sync(DCON_SOURCE_DCON);
|
|
|
+ dcon_set_source_sync(dcon, DCON_SOURCE_DCON);
|
|
|
}
|
|
|
|
|
|
return 0;
|
|
@@ -787,9 +820,11 @@ static int dcon_suspend(struct i2c_client *client, pm_message_t state)
|
|
|
|
|
|
static int dcon_resume(struct i2c_client *client)
|
|
|
{
|
|
|
+ struct dcon_priv *dcon = i2c_get_clientdata(client);
|
|
|
+
|
|
|
if (dcon_sleep_val == DCON_ACTIVE) {
|
|
|
- dcon_bus_stabilize(client, 0);
|
|
|
- dcon_set_source(DCON_SOURCE_CPU);
|
|
|
+ dcon_bus_stabilize(dcon, 0);
|
|
|
+ dcon_set_source(dcon, DCON_SOURCE_CPU);
|
|
|
}
|
|
|
|
|
|
return 0;
|