#include <linux/pm_runtime.h>
#include <linux/delay.h>
#include <linux/semaphore.h>
+#include <linux/pci.h>
#include "i2c-designware-core.h"
static char *abort_sources[] = {
return -EIO;
}
+void (*i2c_dw_fixup_get)(unsigned short pcidev) = NULL;
+void (*i2c_dw_fixup_put)(unsigned short pcidev) = NULL;
/*
* Prepare controller for a transaction and call i2c_dw_xfer_msg
*/
i2c_dw_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
{
struct dw_i2c_dev *dev = i2c_get_adapdata(adap);
+ struct pci_dev *pdev = container_of(dev->dev, struct pci_dev, dev);
int ret;
dev_dbg(dev->dev, "%s: msgs: %d\n", __func__, num);
down(&dev->lock);
+
+ if (i2c_dw_fixup_get)
+ i2c_dw_fixup_get(pdev->device);
+
pm_runtime_get_sync(dev->dev);
INIT_COMPLETION(dev->cmd_complete);
done:
pm_runtime_mark_last_busy(dev->dev);
pm_runtime_put_autosuspend(dev->dev);
+
+ if (i2c_dw_fixup_put)
+ i2c_dw_fixup_put(pdev->device);
+
up(&dev->lock);
return ret;
}
#endif
-#ifdef CONFIG_SUPPORT_TOSHIBA_MIPI_LVDS_BRIDGE
-extern void vlcm_vadd_get(void);
-extern void vlcm_vadd_put(void);
-#endif
-
#endif /* DRIVERS_PCI_H */
DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH6_1, asus_hides_smbus_lpc_ich6_resume);
DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH6_1, asus_hides_smbus_lpc_ich6_resume_early);
-#if CONFIG_SUPPORT_TOSHIBA_MIPI_LVDS_BRIDGE
-static void tc35876x_lvds_panel_fixup_suspend(struct pci_dev *dev)
-{
- vlcm_vadd_put();
-}
-DECLARE_PCI_FIXUP_SUSPEND(PCI_VENDOR_ID_INTEL,
- 0x082E,
- tc35876x_lvds_panel_fixup_suspend);
-
-static void tc35876x_lvds_panel_fixup_resume(struct pci_dev *dev)
-{
- vlcm_vadd_get();
-}
-DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_INTEL,
- 0x082E,
- tc35876x_lvds_panel_fixup_resume);
-#endif
-
/*
* SiS 96x south bridge: BIOS typically hides SMBus device...
*/
usleep_range(1000, 2000);
/* try to turn vadd off */
- vlcm_vadd_put();
+ i2c_dw_fixup_put(I2C_PCI_DEVICE_ID);
}
/* ************************************************************************* *\
printk(KERN_INFO "[DISPLAY ] %s\n", __func__);
/* get vadd count, and make sure vadd is on */
- vlcm_vadd_get();
+ i2c_dw_fixup_get(I2C_PCI_DEVICE_ID);
+
+ /* before enabling GPIO_BL, need wait 200ms */
+ msleep(200);
if (cmi_lcd_i2c_client) {
int ret;
#define PANEL_CM_GAIN 0x711
#define PANEL_CM_HUETABLE_START 0x730
#define PANEL_CM_HUETABLE_END 0x747 /* inclusive */
+#define I2C_PCI_DEVICE_ID 0x082E
extern struct i2c_client *cmi_lcd_i2c_client;
.runtime_idle = psb_runtime_idle,
.suspend = psb_runtime_suspend,
.resume = psb_runtime_resume,
+#ifdef CONFIG_SUPPORT_TOSHIBA_MIPI_LVDS_BRIDGE
+ .suspend_noirq = psb_lvds_panel_suspend_noirq,
+#endif
};
static struct drm_driver driver = {
#ifdef CONFIG_SUPPORT_TOSHIBA_MIPI_LVDS_BRIDGE
DEFINE_MUTEX(vadd_mutex);
static int i2c_access_count;
+struct delayed_work i2c_wa_delayed_wk;
+
+int psb_lvds_panel_suspend_noirq(struct device *dev)
+{
+ /* when system enters S3 while delayed work is not executed, it will
+ * cause vadd keeps on in S3, then high power consumption in S3. to
+ * avoid this, before entering S3, flush delayed work to make sure
+ * there's no pending work */
+ flush_delayed_work_sync(&i2c_wa_delayed_wk);
+ return 0;
+}
/* use access count to mark status of i2c bus 2, and make sure avdd is turned on
* when accessing this i2c. when accaccess count reaches 1, then turn on lvds
* panel's avdd
*/
-void vlcm_vadd_get()
+static void vlcm_vadd_get()
{
mutex_lock(&vadd_mutex);
++i2c_access_count;
pr_err("%s: faild to pull high VADD\n", __func__);
goto unlock;
}
- msleep(260);
+ msleep(60);
}
unlock:
mutex_unlock(&vadd_mutex);
/* decrease reference count, and turn vadd off when count reaches 0
*/
-void vlcm_vadd_put()
+static void vlcm_vadd_put(struct work_struct *work)
{
mutex_lock(&vadd_mutex);
if (i2c_access_count == 0) {
unlock:
mutex_unlock(&vadd_mutex);
}
+
+void psb_i2c_dw_fixup_get(unsigned short pcidev)
+{
+ int ret = 0;
+
+ if (pcidev == I2C_PCI_DEVICE_ID) {
+ ret = cancel_delayed_work(&i2c_wa_delayed_wk);
+ vlcm_vadd_get();
+ /* to make get/put consist, if work is canceled, after another
+ * get, decrease access count */
+ if (ret) {
+ mutex_lock(&vadd_mutex);
+ --i2c_access_count;
+ mutex_unlock(&vadd_mutex);
+ }
+ }
+}
+
+void psb_i2c_dw_fixup_put(unsigned short pcidev)
+{
+ if (pcidev == I2C_PCI_DEVICE_ID) {
+ /*
+ * before the scecond accessing i2c, access count will be 2,
+ * flush work will cause access count descres to 1, then
+ * the latter schedule_work can be delayed to 100ms.
+ */
+ flush_delayed_work_sync(&i2c_wa_delayed_wk);
+ schedule_delayed_work(&i2c_wa_delayed_wk, HZ/10);
+ }
+}
+
+static int __init i2c_workaround_init(void)
+{
+ INIT_DELAYED_WORK(&i2c_wa_delayed_wk, vlcm_vadd_put);
+ i2c_dw_fixup_get = psb_i2c_dw_fixup_get;
+ i2c_dw_fixup_put = psb_i2c_dw_fixup_put;
+
+ return 0;
+}
+arch_initcall(i2c_workaround_init);
#endif
+
void mdfld_reset_panel_handler_work(struct work_struct *work)
{
struct drm_psb_private *dev_priv =
void release_ospm_lock(void);
#ifdef CONFIG_SUPPORT_TOSHIBA_MIPI_LVDS_BRIDGE
-extern void vlcm_vadd_get(void);
-extern void vlcm_vadd_put(void);
+extern void (*i2c_dw_fixup_get)(unsigned short pcidev);
+extern void (*i2c_dw_fixup_put)(unsigned short pcidev);
+extern int psb_lvds_panel_suspend_noirq(struct device *dev);
#endif
#endif /*_PSB_POWERMGMT_H_*/