Commit 537d8478 authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'for-linus' of git://gitorious.org/linux-omap-dss2/linux

* 'for-linus' of git://gitorious.org/linux-omap-dss2/linux: (64 commits)
  OMAP: DSS2: OMAPFB: add support for FBIO_WAITFORVSYNC
  OMAP: DSS2: Replace strncmp() with sysfs_streq() in overlay_manager_store()
  OMAP: DSS2: Fix error path in omap_dsi_update()
  OMAP: DSS2: TDO35S: fix video signaling
  OMAP: DSS2: OMAPFB: Fix invalid bpp for PAL and NTSC modes
  OMAP: DSS2: OMAPFB: Fix probe error path
  OMAP3EVM: Replace vdvi regulator supply with vdds_dsi
  OMAP: DSS2: Remove extra return statement
  OMAP: DSS2: adjust YUV overlay width to be even
  OMAP: DSS2: OMAPFB: Fix sysfs mirror input check
  OMAP: DSS2: OMAPFB: Remove redundant color register range check
  OMAP: DSS2: OMAPFB: Remove redundant rotate range check
  OMAP: DSS2: OMAPFB: Check fb2display() return value
  OMAP: DSS2: Taal: Optimize enable_te, rotate, mirror when value unchanged
  OMAP: DSS2: DSI: detect unsupported update requests
  OMAP: DSS2: DSI: increase FIFO low threshold
  OMAP: DSS2: DSI: Add error IRQ mask for DSI complexIO
  OMAP: DSS2: DSI: Remove BTA after set_max_rx_packet_size
  OMAP: DSS2: change manual update scaling setup
  OMAP: DSS2: DSI: use BTA to end the frame transfer
  ...
parents d71048e2 ece350d3
...@@ -514,14 +514,11 @@ static struct regulator_init_data omap3_evm_vdac = { ...@@ -514,14 +514,11 @@ static struct regulator_init_data omap3_evm_vdac = {
}; };
/* VPLL2 for digital video outputs */ /* VPLL2 for digital video outputs */
static struct regulator_consumer_supply omap3_evm_vpll2_supply = { static struct regulator_consumer_supply omap3_evm_vpll2_supply =
.supply = "vdvi", REGULATOR_SUPPLY("vdds_dsi", "omapdss");
.dev = &omap3_evm_lcd_device.dev,
};
static struct regulator_init_data omap3_evm_vpll2 = { static struct regulator_init_data omap3_evm_vpll2 = {
.constraints = { .constraints = {
.name = "VDVI",
.min_uV = 1800000, .min_uV = 1800000,
.max_uV = 1800000, .max_uV = 1800000,
.apply_uV = true, .apply_uV = true,
......
...@@ -238,7 +238,7 @@ int dsi_vc_dcs_write_1(int channel, u8 dcs_cmd, u8 param); ...@@ -238,7 +238,7 @@ int dsi_vc_dcs_write_1(int channel, u8 dcs_cmd, u8 param);
int dsi_vc_dcs_write_nosync(int channel, u8 *data, int len); int dsi_vc_dcs_write_nosync(int channel, u8 *data, int len);
int dsi_vc_dcs_read(int channel, u8 dcs_cmd, u8 *buf, int buflen); int dsi_vc_dcs_read(int channel, u8 dcs_cmd, u8 *buf, int buflen);
int dsi_vc_dcs_read_1(int channel, u8 dcs_cmd, u8 *data); int dsi_vc_dcs_read_1(int channel, u8 dcs_cmd, u8 *data);
int dsi_vc_dcs_read_2(int channel, u8 dcs_cmd, u16 *data); int dsi_vc_dcs_read_2(int channel, u8 dcs_cmd, u8 *data1, u8 *data2);
int dsi_vc_set_max_rx_packet_size(int channel, u16 len); int dsi_vc_set_max_rx_packet_size(int channel, u16 len);
int dsi_vc_send_null(int channel); int dsi_vc_send_null(int channel);
int dsi_vc_send_bta_sync(int channel); int dsi_vc_send_bta_sync(int channel);
...@@ -277,8 +277,8 @@ struct omap_video_timings { ...@@ -277,8 +277,8 @@ struct omap_video_timings {
* identify the mode, and does not actually use the configs * identify the mode, and does not actually use the configs
* itself. However, the configs should be something that * itself. However, the configs should be something that
* a normal monitor can also show */ * a normal monitor can also show */
const extern struct omap_video_timings omap_dss_pal_timings; extern const struct omap_video_timings omap_dss_pal_timings;
const extern struct omap_video_timings omap_dss_ntsc_timings; extern const struct omap_video_timings omap_dss_ntsc_timings;
#endif #endif
struct omap_overlay_info { struct omap_overlay_info {
...@@ -560,7 +560,8 @@ void omapdss_dsi_vc_enable_hs(int channel, bool enable); ...@@ -560,7 +560,8 @@ void omapdss_dsi_vc_enable_hs(int channel, bool enable);
int omapdss_dsi_enable_te(struct omap_dss_device *dssdev, bool enable); int omapdss_dsi_enable_te(struct omap_dss_device *dssdev, bool enable);
int omap_dsi_prepare_update(struct omap_dss_device *dssdev, int omap_dsi_prepare_update(struct omap_dss_device *dssdev,
u16 *x, u16 *y, u16 *w, u16 *h); u16 *x, u16 *y, u16 *w, u16 *h,
bool enlarge_update_area);
int omap_dsi_update(struct omap_dss_device *dssdev, int omap_dsi_update(struct omap_dss_device *dssdev,
int channel, int channel,
u16 x, u16 y, u16 w, u16 h, u16 x, u16 y, u16 w, u16 h,
......
#ifndef __ARCH_ARM_PLAT_OMAP_NOKIA_DSI_PANEL_H
#define __ARCH_ARM_PLAT_OMAP_NOKIA_DSI_PANEL_H
#include "display.h"
/**
* struct nokia_dsi_panel_data - Nokia DSI panel driver configuration
* @name: panel name
* @use_ext_te: use external TE
* @ext_te_gpio: external TE GPIO
* @use_esd_check: perform ESD checks
* @max_backlight_level: maximum backlight level
* @set_backlight: pointer to backlight set function
* @get_backlight: pointer to backlight get function
*/
struct nokia_dsi_panel_data {
const char *name;
int reset_gpio;
bool use_ext_te;
int ext_te_gpio;
bool use_esd_check;
int max_backlight_level;
int (*set_backlight)(struct omap_dss_device *dssdev, int level);
int (*get_backlight)(struct omap_dss_device *dssdev);
};
#endif /* __ARCH_ARM_PLAT_OMAP_NOKIA_DSI_PANEL_H */
...@@ -28,12 +28,13 @@ ...@@ -28,12 +28,13 @@
#include <linux/fb.h> #include <linux/fb.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/gpio.h> #include <linux/gpio.h>
#include <linux/completion.h>
#include <linux/workqueue.h> #include <linux/workqueue.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/regulator/consumer.h>
#include <linux/mutex.h> #include <linux/mutex.h>
#include <plat/display.h> #include <plat/display.h>
#include <plat/nokia-dsi-panel.h>
/* DSI Virtual channel. Hardcoded for now. */ /* DSI Virtual channel. Hardcoded for now. */
#define TCH 0 #define TCH 0
...@@ -62,11 +63,136 @@ ...@@ -62,11 +63,136 @@
#define DCS_GET_ID2 0xdb #define DCS_GET_ID2 0xdb
#define DCS_GET_ID3 0xdc #define DCS_GET_ID3 0xdc
/* #define TAAL_USE_ESD_CHECK */
#define TAAL_ESD_CHECK_PERIOD msecs_to_jiffies(5000) #define TAAL_ESD_CHECK_PERIOD msecs_to_jiffies(5000)
static irqreturn_t taal_te_isr(int irq, void *data);
static void taal_te_timeout_work_callback(struct work_struct *work);
static int _taal_enable_te(struct omap_dss_device *dssdev, bool enable); static int _taal_enable_te(struct omap_dss_device *dssdev, bool enable);
struct panel_regulator {
struct regulator *regulator;
const char *name;
int min_uV;
int max_uV;
};
static void free_regulators(struct panel_regulator *regulators, int n)
{
int i;
for (i = 0; i < n; i++) {
/* disable/put in reverse order */
regulator_disable(regulators[n - i - 1].regulator);
regulator_put(regulators[n - i - 1].regulator);
}
}
static int init_regulators(struct omap_dss_device *dssdev,
struct panel_regulator *regulators, int n)
{
int r, i, v;
for (i = 0; i < n; i++) {
struct regulator *reg;
reg = regulator_get(&dssdev->dev, regulators[i].name);
if (IS_ERR(reg)) {
dev_err(&dssdev->dev, "failed to get regulator %s\n",
regulators[i].name);
r = PTR_ERR(reg);
goto err;
}
/* FIXME: better handling of fixed vs. variable regulators */
v = regulator_get_voltage(reg);
if (v < regulators[i].min_uV || v > regulators[i].max_uV) {
r = regulator_set_voltage(reg, regulators[i].min_uV,
regulators[i].max_uV);
if (r) {
dev_err(&dssdev->dev,
"failed to set regulator %s voltage\n",
regulators[i].name);
regulator_put(reg);
goto err;
}
}
r = regulator_enable(reg);
if (r) {
dev_err(&dssdev->dev, "failed to enable regulator %s\n",
regulators[i].name);
regulator_put(reg);
goto err;
}
regulators[i].regulator = reg;
}
return 0;
err:
free_regulators(regulators, i);
return r;
}
/**
* struct panel_config - panel configuration
* @name: panel name
* @type: panel type
* @timings: panel resolution
* @sleep: various panel specific delays, passed to msleep() if non-zero
* @reset_sequence: reset sequence timings, passed to udelay() if non-zero
* @regulators: array of panel regulators
* @num_regulators: number of regulators in the array
*/
struct panel_config {
const char *name;
int type;
struct omap_video_timings timings;
struct {
unsigned int sleep_in;
unsigned int sleep_out;
unsigned int hw_reset;
unsigned int enable_te;
} sleep;
struct {
unsigned int high;
unsigned int low;
} reset_sequence;
struct panel_regulator *regulators;
int num_regulators;
};
enum {
PANEL_TAAL,
};
static struct panel_config panel_configs[] = {
{
.name = "taal",
.type = PANEL_TAAL,
.timings = {
.x_res = 864,
.y_res = 480,
},
.sleep = {
.sleep_in = 5,
.sleep_out = 5,
.hw_reset = 5,
.enable_te = 100, /* possible panel bug */
},
.reset_sequence = {
.high = 10,
.low = 10,
},
},
};
struct taal_data { struct taal_data {
struct mutex lock; struct mutex lock;
...@@ -84,8 +210,15 @@ struct taal_data { ...@@ -84,8 +210,15 @@ struct taal_data {
bool mirror; bool mirror;
bool te_enabled; bool te_enabled;
bool use_ext_te;
struct completion te_completion; atomic_t do_update;
struct {
u16 x;
u16 y;
u16 w;
u16 h;
} update_region;
struct delayed_work te_timeout_work;
bool use_dsi_bl; bool use_dsi_bl;
...@@ -96,8 +229,16 @@ struct taal_data { ...@@ -96,8 +229,16 @@ struct taal_data {
struct workqueue_struct *esd_wq; struct workqueue_struct *esd_wq;
struct delayed_work esd_work; struct delayed_work esd_work;
struct panel_config *panel_config;
}; };
static inline struct nokia_dsi_panel_data
*get_panel_data(const struct omap_dss_device *dssdev)
{
return (struct nokia_dsi_panel_data *) dssdev->data;
}
static void taal_esd_work(struct work_struct *work); static void taal_esd_work(struct work_struct *work);
static void hw_guard_start(struct taal_data *td, int guard_msec) static void hw_guard_start(struct taal_data *td, int guard_msec)
...@@ -159,7 +300,8 @@ static int taal_sleep_in(struct taal_data *td) ...@@ -159,7 +300,8 @@ static int taal_sleep_in(struct taal_data *td)
hw_guard_start(td, 120); hw_guard_start(td, 120);
msleep(5); if (td->panel_config->sleep.sleep_in)
msleep(td->panel_config->sleep.sleep_in);
return 0; return 0;
} }
...@@ -176,7 +318,8 @@ static int taal_sleep_out(struct taal_data *td) ...@@ -176,7 +318,8 @@ static int taal_sleep_out(struct taal_data *td)
hw_guard_start(td, 120); hw_guard_start(td, 120);
msleep(5); if (td->panel_config->sleep.sleep_out)
msleep(td->panel_config->sleep.sleep_out);
return 0; return 0;
} }
...@@ -279,6 +422,7 @@ static int taal_bl_update_status(struct backlight_device *dev) ...@@ -279,6 +422,7 @@ static int taal_bl_update_status(struct backlight_device *dev)
{ {
struct omap_dss_device *dssdev = dev_get_drvdata(&dev->dev); struct omap_dss_device *dssdev = dev_get_drvdata(&dev->dev);
struct taal_data *td = dev_get_drvdata(&dssdev->dev); struct taal_data *td = dev_get_drvdata(&dssdev->dev);
struct nokia_dsi_panel_data *panel_data = get_panel_data(dssdev);
int r; int r;
int level; int level;
...@@ -290,24 +434,26 @@ static int taal_bl_update_status(struct backlight_device *dev) ...@@ -290,24 +434,26 @@ static int taal_bl_update_status(struct backlight_device *dev)
dev_dbg(&dssdev->dev, "update brightness to %d\n", level); dev_dbg(&dssdev->dev, "update brightness to %d\n", level);
mutex_lock(&td->lock);
if (td->use_dsi_bl) { if (td->use_dsi_bl) {
if (td->enabled) { if (td->enabled) {
dsi_bus_lock(); dsi_bus_lock();
r = taal_dcs_write_1(DCS_BRIGHTNESS, level); r = taal_dcs_write_1(DCS_BRIGHTNESS, level);
dsi_bus_unlock(); dsi_bus_unlock();
if (r) } else {
return r; r = 0;
} }
} else { } else {
if (!dssdev->set_backlight) if (!panel_data->set_backlight)
return -EINVAL; r = -EINVAL;
else
r = dssdev->set_backlight(dssdev, level); r = panel_data->set_backlight(dssdev, level);
if (r)
return r;
} }
return 0; mutex_unlock(&td->lock);
return r;
} }
static int taal_bl_get_intensity(struct backlight_device *dev) static int taal_bl_get_intensity(struct backlight_device *dev)
...@@ -344,16 +490,6 @@ static void taal_get_resolution(struct omap_dss_device *dssdev, ...@@ -344,16 +490,6 @@ static void taal_get_resolution(struct omap_dss_device *dssdev,
} }
} }
static irqreturn_t taal_te_isr(int irq, void *data)
{
struct omap_dss_device *dssdev = data;
struct taal_data *td = dev_get_drvdata(&dssdev->dev);
complete_all(&td->te_completion);
return IRQ_HANDLED;
}
static ssize_t taal_num_errors_show(struct device *dev, static ssize_t taal_num_errors_show(struct device *dev,
struct device_attribute *attr, char *buf) struct device_attribute *attr, char *buf)
{ {
...@@ -362,6 +498,8 @@ static ssize_t taal_num_errors_show(struct device *dev, ...@@ -362,6 +498,8 @@ static ssize_t taal_num_errors_show(struct device *dev,
u8 errors; u8 errors;
int r; int r;
mutex_lock(&td->lock);
if (td->enabled) { if (td->enabled) {
dsi_bus_lock(); dsi_bus_lock();
r = taal_dcs_read_1(DCS_READ_NUM_ERRORS, &errors); r = taal_dcs_read_1(DCS_READ_NUM_ERRORS, &errors);
...@@ -370,6 +508,8 @@ static ssize_t taal_num_errors_show(struct device *dev, ...@@ -370,6 +508,8 @@ static ssize_t taal_num_errors_show(struct device *dev,
r = -ENODEV; r = -ENODEV;
} }
mutex_unlock(&td->lock);
if (r) if (r)
return r; return r;
...@@ -384,6 +524,8 @@ static ssize_t taal_hw_revision_show(struct device *dev, ...@@ -384,6 +524,8 @@ static ssize_t taal_hw_revision_show(struct device *dev,
u8 id1, id2, id3; u8 id1, id2, id3;
int r; int r;
mutex_lock(&td->lock);
if (td->enabled) { if (td->enabled) {
dsi_bus_lock(); dsi_bus_lock();
r = taal_get_id(&id1, &id2, &id3); r = taal_get_id(&id1, &id2, &id3);
...@@ -392,6 +534,8 @@ static ssize_t taal_hw_revision_show(struct device *dev, ...@@ -392,6 +534,8 @@ static ssize_t taal_hw_revision_show(struct device *dev,
r = -ENODEV; r = -ENODEV;
} }
mutex_unlock(&td->lock);
if (r) if (r)
return r; return r;
...@@ -441,6 +585,8 @@ static ssize_t store_cabc_mode(struct device *dev, ...@@ -441,6 +585,8 @@ static ssize_t store_cabc_mode(struct device *dev,
if (i == ARRAY_SIZE(cabc_modes)) if (i == ARRAY_SIZE(cabc_modes))
return -EINVAL; return -EINVAL;
mutex_lock(&td->lock);
if (td->enabled) { if (td->enabled) {
dsi_bus_lock(); dsi_bus_lock();
if (!td->cabc_broken) if (!td->cabc_broken)
...@@ -450,6 +596,8 @@ static ssize_t store_cabc_mode(struct device *dev, ...@@ -450,6 +596,8 @@ static ssize_t store_cabc_mode(struct device *dev,
td->cabc_mode = i; td->cabc_mode = i;
mutex_unlock(&td->lock);
return count; return count;
} }
...@@ -488,47 +636,93 @@ static struct attribute_group taal_attr_group = { ...@@ -488,47 +636,93 @@ static struct attribute_group taal_attr_group = {
.attrs = taal_attrs, .attrs = taal_attrs,
}; };
static void taal_hw_reset(struct omap_dss_device *dssdev)
{
struct taal_data *td = dev_get_drvdata(&dssdev->dev);
struct nokia_dsi_panel_data *panel_data = get_panel_data(dssdev);
if (panel_data->reset_gpio == -1)
return;
gpio_set_value(panel_data->reset_gpio, 1);
if (td->panel_config->reset_sequence.high)
udelay(td->panel_config->reset_sequence.high);
/* reset the panel */
gpio_set_value(panel_data->reset_gpio, 0);
/* assert reset */
if (td->panel_config->reset_sequence.low)
udelay(td->panel_config->reset_sequence.low);
gpio_set_value(panel_data->reset_gpio, 1);
/* wait after releasing reset */
if (td->panel_config->sleep.hw_reset)
msleep(td->panel_config->sleep.hw_reset);
}
static int taal_probe(struct omap_dss_device *dssdev) static int taal_probe(struct omap_dss_device *dssdev)
{ {
struct backlight_properties props; struct backlight_properties props;
struct taal_data *td; struct taal_data *td;
struct backlight_device *bldev; struct backlight_device *bldev;
int r; struct nokia_dsi_panel_data *panel_data = get_panel_data(dssdev);
struct panel_config *panel_config = NULL;
const struct omap_video_timings taal_panel_timings = { int r, i;
.x_res = 864,
.y_res = 480,
};
dev_dbg(&dssdev->dev, "probe\n"); dev_dbg(&dssdev->dev, "probe\n");
if (!panel_data || !panel_data->name) {
r = -EINVAL;
goto err;
}
for (i = 0; i < ARRAY_SIZE(panel_configs); i++) {
if (strcmp(panel_data->name, panel_configs[i].name) == 0) {
panel_config = &panel_configs[i];
break;
}
}
if (!panel_config) {
r = -EINVAL;
goto err;
}
dssdev->panel.config = OMAP_DSS_LCD_TFT; dssdev->panel.config = OMAP_DSS_LCD_TFT;
dssdev->panel.timings = taal_panel_timings; dssdev->panel.timings = panel_config->timings;
dssdev->ctrl.pixel_size = 24; dssdev->ctrl.pixel_size = 24;
td = kzalloc(sizeof(*td), GFP_KERNEL); td = kzalloc(sizeof(*td), GFP_KERNEL);
if (!td) { if (!td) {
r = -ENOMEM; r = -ENOMEM;
goto err0; goto err;
} }
td->dssdev = dssdev; td->dssdev = dssdev;
td->panel_config = panel_config;
mutex_init(&td->lock); mutex_init(&td->lock);
atomic_set(&td->do_update, 0);
r = init_regulators(dssdev, panel_config->regulators,
panel_config->num_regulators);
if (r)
goto err_reg;
td->esd_wq = create_singlethread_workqueue("taal_esd"); td->esd_wq = create_singlethread_workqueue("taal_esd");
if (td->esd_wq == NULL) { if (td->esd_wq == NULL) {
dev_err(&dssdev->dev, "can't create ESD workqueue\n"); dev_err(&dssdev->dev, "can't create ESD workqueue\n");
r = -ENOMEM; r = -ENOMEM;
goto err1; goto err_wq;
} }
INIT_DELAYED_WORK_DEFERRABLE(&td->esd_work, taal_esd_work); INIT_DELAYED_WORK_DEFERRABLE(&td->esd_work, taal_esd_work);
dev_set_drvdata(&dssdev->dev, td); dev_set_drvdata(&dssdev->dev, td);
taal_hw_reset(dssdev);
/* if no platform set_backlight() defined, presume DSI backlight /* if no platform set_backlight() defined, presume DSI backlight
* control */ * control */
memset(&props, 0, sizeof(struct backlight_properties)); memset(&props, 0, sizeof(struct backlight_properties));
if (!dssdev->set_backlight) if (!panel_data->set_backlight)
td->use_dsi_bl = true; td->use_dsi_bl = true;
if (td->use_dsi_bl) if (td->use_dsi_bl)
...@@ -539,7 +733,7 @@ static int taal_probe(struct omap_dss_device *dssdev) ...@@ -539,7 +733,7 @@ static int taal_probe(struct omap_dss_device *dssdev)
&taal_bl_ops, &props); &taal_bl_ops, &props);
if (IS_ERR(bldev)) { if (IS_ERR(bldev)) {
r = PTR_ERR(bldev); r = PTR_ERR(bldev);
goto err2; goto err_bl;
} }
td->bldev = bldev; td->bldev = bldev;
...@@ -553,13 +747,13 @@ static int taal_probe(struct omap_dss_device *dssdev) ...@@ -553,13 +747,13 @@ static int taal_probe(struct omap_dss_device *dssdev)
taal_bl_update_status(bldev); taal_bl_update_status(bldev);
if (dssdev->phy.dsi.ext_te) { if (panel_data->use_ext_te) {
int gpio = dssdev->phy.dsi.ext_te_gpio; int gpio = panel_data->ext_te_gpio;
r = gpio_request(gpio, "taal irq"); r = gpio_request(gpio, "taal irq");
if (r) { if (r) {
dev_err(&dssdev->dev, "GPIO request failed\n"); dev_err(&dssdev->dev, "GPIO request failed\n");
goto err3; goto err_gpio;
} }
gpio_direction_input(gpio); gpio_direction_input(gpio);
...@@ -571,49 +765,52 @@ static int taal_probe(struct omap_dss_device *dssdev) ...@@ -571,49 +765,52 @@ static int taal_probe(struct omap_dss_device *dssdev)
if (r) { if (r) {
dev_err(&dssdev->dev, "IRQ request failed\n"); dev_err(&dssdev->dev, "IRQ request failed\n");
gpio_free(gpio); gpio_free(gpio);
goto err3; goto err_irq;
} }
init_completion(&td->te_completion); INIT_DELAYED_WORK_DEFERRABLE(&td->te_timeout_work,
taal_te_timeout_work_callback);
td->use_ext_te = true; dev_dbg(&dssdev->dev, "Using GPIO TE\n");
} }
r = sysfs_create_group(&dssdev->dev.kobj, &taal_attr_group); r = sysfs_create_group(&dssdev->dev.kobj, &taal_attr_group);
if (r) { if (r) {
dev_err(&dssdev->dev, "failed to create sysfs files\n"); dev_err(&dssdev->dev, "failed to create sysfs files\n");
goto err4; goto err_sysfs;
} }
return 0; return 0;
err4: err_sysfs:
if (td->use_ext_te) { if (panel_data->use_ext_te)
int gpio = dssdev->phy.dsi.ext_te_gpio; free_irq(gpio_to_irq(panel_data->ext_te_gpio), dssdev);
free_irq(gpio_to_irq(gpio), dssdev); err_irq:
gpio_free(gpio); if (panel_data->use_ext_te)
} gpio_free(panel_data->ext_te_gpio);
err3: err_gpio:
backlight_device_unregister(bldev); backlight_device_unregister(bldev);
err2: err_bl:
cancel_delayed_work_sync(&td->esd_work);
destroy_workqueue(td->esd_wq); destroy_workqueue(td->esd_wq);
err1: err_wq:
free_regulators(panel_config->regulators, panel_config->num_regulators);
err_reg:
kfree(td); kfree(td);
err0: err:
return r; return r;
} }
static void taal_remove(struct omap_dss_device *dssdev) static void taal_remove(struct omap_dss_device *dssdev)
{ {
struct taal_data *td = dev_get_drvdata(&dssdev->dev); struct taal_data *td = dev_get_drvdata(&dssdev->dev);
struct nokia_dsi_panel_data *panel_data = get_panel_data(dssdev);
struct backlight_device *bldev; struct backlight_device *bldev;
dev_dbg(&dssdev->dev, "remove\n"); dev_dbg(&dssdev->dev, "remove\n");
sysfs_remove_group(&dssdev->dev.kobj, &taal_attr_group); sysfs_remove_group(&dssdev->dev.kobj, &taal_attr_group);
if (td->use_ext_te) { if (panel_data->use_ext_te) {
int gpio = dssdev->phy.dsi.ext_te_gpio; int gpio = panel_data->ext_te_gpio;
free_irq(gpio_to_irq(gpio), dssdev); free_irq(gpio_to_irq(gpio), dssdev);
gpio_free(gpio); gpio_free(gpio);
} }
...@@ -623,9 +820,15 @@ static void taal_remove(struct omap_dss_device *dssdev) ...@@ -623,9 +820,15 @@ static void taal_remove(struct omap_dss_device *dssdev)
taal_bl_update_status(bldev); taal_bl_update_status(bldev);
backlight_device_unregister(bldev); backlight_device_unregister(bldev);
cancel_delayed_work_sync(&td->esd_work); cancel_delayed_work(&td->esd_work);
destroy_workqueue(td->esd_wq); destroy_workqueue(td->esd_wq);
/* reset, to be sure that the panel is in a valid state */
taal_hw_reset(dssdev);
free_regulators(td->panel_config->regulators,
td->panel_config->num_regulators);
kfree(td); kfree(td);
} }
...@@ -635,23 +838,14 @@ static int taal_power_on(struct omap_dss_device *dssdev) ...@@ -635,23 +838,14 @@ static int taal_power_on(struct omap_dss_device *dssdev)
u8 id1, id2, id3; u8 id1, id2, id3;
int r; int r;
if (dssdev->platform_enable) {
r = dssdev->platform_enable(dssdev);
if (r)
return r;
}
/* it seems we have to wait a bit until taal is ready */
msleep(5);
dsi_bus_lock();
r = omapdss_dsi_display_enable(dssdev); r = omapdss_dsi_display_enable(dssdev);
if (r) { if (r) {
dev_err(&dssdev->dev, "failed to enable DSI\n"); dev_err(&dssdev->dev, "failed to enable DSI\n");
goto err0; goto err0;
} }
taal_hw_reset(dssdev);
omapdss_dsi_vc_enable_hs(TCH, false); omapdss_dsi_vc_enable_hs(TCH, false);
r = taal_sleep_out(td); r = taal_sleep_out(td);
...@@ -662,34 +856,47 @@ static int taal_power_on(struct omap_dss_device *dssdev) ...@@ -662,34 +856,47 @@ static int taal_power_on(struct omap_dss_device *dssdev)
if (r) if (r)
goto err; goto err;
/* on early revisions CABC is broken */ /* on early Taal revisions CABC is broken */
if (id2 == 0x00 || id2 == 0xff || id2 == 0x81) if (td->panel_config->type == PANEL_TAAL &&
(id2 == 0x00 || id2 == 0xff || id2 == 0x81))
td->cabc_broken = true; td->cabc_broken = true;
taal_dcs_write_1(DCS_BRIGHTNESS, 0xff); r = taal_dcs_write_1(DCS_BRIGHTNESS, 0xff);
taal_dcs_write_1(DCS_CTRL_DISPLAY, (1<<2) | (1<<5)); /* BL | BCTRL */ if (r)
goto err;
r = taal_dcs_write_1(DCS_CTRL_DISPLAY,
(1<<2) | (1<<5)); /* BL | BCTRL */
if (r)
goto err;
r = taal_dcs_write_1(DCS_PIXEL_FORMAT, 0x7); /* 24bit/pixel */
if (r)
goto err;
taal_dcs_write_1(DCS_PIXEL_FORMAT, 0x7); /* 24bit/pixel */ r = taal_set_addr_mode(td->rotate, td->mirror);
if (r)
goto err;
taal_set_addr_mode(td->rotate, td->mirror); if (!td->cabc_broken) {
if (!td->cabc_broken) r = taal_dcs_write_1(DCS_WRITE_CABC, td->cabc_mode);
taal_dcs_write_1(DCS_WRITE_CABC, td->cabc_mode); if (r)
goto err;
}
taal_dcs_write_0(DCS_DISPLAY_ON); r = taal_dcs_write_0(DCS_DISPLAY_ON);
if (r)
goto err;
r = _taal_enable_te(dssdev, td->te_enabled); r = _taal_enable_te(dssdev, td->te_enabled);
if (r) if (r)
goto err; goto err;
#ifdef TAAL_USE_ESD_CHECK
queue_delayed_work(td->esd_wq, &td->esd_work, TAAL_ESD_CHECK_PERIOD);
#endif
td->enabled = 1; td->enabled = 1;
if (!td->intro_printed) { if (!td->intro_printed) {
dev_info(&dssdev->dev, "revision %02x.%02x.%02x\n", dev_info(&dssdev->dev, "%s panel revision %02x.%02x.%02x\n",
id1, id2, id3); td->panel_config->name, id1, id2, id3);
if (td->cabc_broken) if (td->cabc_broken)
dev_info(&dssdev->dev, dev_info(&dssdev->dev,
"old Taal version, CABC disabled\n"); "old Taal version, CABC disabled\n");
...@@ -698,46 +905,44 @@ static int taal_power_on(struct omap_dss_device *dssdev) ...@@ -698,46 +905,44 @@ static int taal_power_on(struct omap_dss_device *dssdev)
omapdss_dsi_vc_enable_hs(TCH, true); omapdss_dsi_vc_enable_hs(TCH, true);
dsi_bus_unlock();
return 0; return 0;
err: err:
dev_err(&dssdev->dev, "error while enabling panel, issuing HW reset\n");
taal_hw_reset(dssdev);
omapdss_dsi_display_disable(dssdev); omapdss_dsi_display_disable(dssdev);
err0: err0:
dsi_bus_unlock();
if (dssdev->platform_disable)
dssdev->platform_disable(dssdev);
return r; return r;
} }
static void taal_power_off(struct omap_dss_device *dssdev) static void taal_power_off(struct omap_dss_device *dssdev)
{ {
struct taal_data *td = dev_get_drvdata(&dssdev->dev); struct taal_data *td = dev_get_drvdata(&dssdev->dev);
int r;
dsi_bus_lock(); r = taal_dcs_write_0(DCS_DISPLAY_OFF);
if (!r) {
cancel_delayed_work(&td->esd_work); r = taal_sleep_in(td);
/* HACK: wait a bit so that the message goes through */
taal_dcs_write_0(DCS_DISPLAY_OFF); msleep(10);
taal_sleep_in(td); }
/* wait a bit so that the message goes through */ if (r) {
msleep(10); dev_err(&dssdev->dev,
"error disabling panel, issuing HW reset\n");
taal_hw_reset(dssdev);
}
omapdss_dsi_display_disable(dssdev); omapdss_dsi_display_disable(dssdev);
if (dssdev->platform_disable)
dssdev->platform_disable(dssdev);
td->enabled = 0; td->enabled = 0;
dsi_bus_unlock();
} }
static int taal_enable(struct omap_dss_device *dssdev) static int taal_enable(struct omap_dss_device *dssdev)
{ {
struct taal_data *td = dev_get_drvdata(&dssdev->dev); struct taal_data *td = dev_get_drvdata(&dssdev->dev);
struct nokia_dsi_panel_data *panel_data = get_panel_data(dssdev);
int r; int r;
dev_dbg(&dssdev->dev, "enable\n"); dev_dbg(&dssdev->dev, "enable\n");
...@@ -749,10 +954,19 @@ static int taal_enable(struct omap_dss_device *dssdev) ...@@ -749,10 +954,19 @@ static int taal_enable(struct omap_dss_device *dssdev)
goto err; goto err;
} }
dsi_bus_lock();
r = taal_power_on(dssdev); r = taal_power_on(dssdev);
dsi_bus_unlock();
if (r) if (r)
goto err; goto err;
if (panel_data->use_esd_check)
queue_delayed_work(td->esd_wq, &td->esd_work,
TAAL_ESD_CHECK_PERIOD);
dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
mutex_unlock(&td->lock); mutex_unlock(&td->lock);
...@@ -772,9 +986,15 @@ static void taal_disable(struct omap_dss_device *dssdev) ...@@ -772,9 +986,15 @@ static void taal_disable(struct omap_dss_device *dssdev)
mutex_lock(&td->lock); mutex_lock(&td->lock);
cancel_delayed_work(&td->esd_work);
dsi_bus_lock();
if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE) if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE)
taal_power_off(dssdev); taal_power_off(dssdev);
dsi_bus_unlock();
dssdev->state = OMAP_DSS_DISPLAY_DISABLED; dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
mutex_unlock(&td->lock); mutex_unlock(&td->lock);
...@@ -794,7 +1014,14 @@ static int taal_suspend(struct omap_dss_device *dssdev) ...@@ -794,7 +1014,14 @@ static int taal_suspend(struct omap_dss_device *dssdev)
goto err; goto err;
} }
cancel_delayed_work(&td->esd_work);
dsi_bus_lock();
taal_power_off(dssdev); taal_power_off(dssdev);
dsi_bus_unlock();
dssdev->state = OMAP_DSS_DISPLAY_SUSPENDED; dssdev->state = OMAP_DSS_DISPLAY_SUSPENDED;
mutex_unlock(&td->lock); mutex_unlock(&td->lock);
...@@ -808,6 +1035,7 @@ static int taal_suspend(struct omap_dss_device *dssdev) ...@@ -808,6 +1035,7 @@ static int taal_suspend(struct omap_dss_device *dssdev)
static int taal_resume(struct omap_dss_device *dssdev) static int taal_resume(struct omap_dss_device *dssdev)
{ {
struct taal_data *td = dev_get_drvdata(&dssdev->dev); struct taal_data *td = dev_get_drvdata(&dssdev->dev);
struct nokia_dsi_panel_data *panel_data = get_panel_data(dssdev);
int r; int r;
dev_dbg(&dssdev->dev, "resume\n"); dev_dbg(&dssdev->dev, "resume\n");
...@@ -819,8 +1047,20 @@ static int taal_resume(struct omap_dss_device *dssdev) ...@@ -819,8 +1047,20 @@ static int taal_resume(struct omap_dss_device *dssdev)
goto err; goto err;
} }
dsi_bus_lock();
r = taal_power_on(dssdev); r = taal_power_on(dssdev);
dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
dsi_bus_unlock();
if (r) {
dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
} else {
dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
if (panel_data->use_esd_check)
queue_delayed_work(td->esd_wq, &td->esd_work,
TAAL_ESD_CHECK_PERIOD);
}
mutex_unlock(&td->lock); mutex_unlock(&td->lock);
...@@ -837,10 +1077,52 @@ static void taal_framedone_cb(int err, void *data) ...@@ -837,10 +1077,52 @@ static void taal_framedone_cb(int err, void *data)
dsi_bus_unlock(); dsi_bus_unlock();
} }
static irqreturn_t taal_te_isr(int irq, void *data)
{
struct omap_dss_device *dssdev = data;
struct taal_data *td = dev_get_drvdata(&dssdev->dev);
int old;
int r;
old = atomic_cmpxchg(&td->do_update, 1, 0);
if (old) {
cancel_delayed_work(&td->te_timeout_work);
r = omap_dsi_update(dssdev, TCH,
td->update_region.x,
td->update_region.y,
td->update_region.w,
td->update_region.h,
taal_framedone_cb, dssdev);
if (r)
goto err;
}
return IRQ_HANDLED;
err:
dev_err(&dssdev->dev, "start update failed\n");
dsi_bus_unlock();
return IRQ_HANDLED;
}
static void taal_te_timeout_work_callback(struct work_struct *work)
{
struct taal_data *td = container_of(work, struct taal_data,
te_timeout_work.work);
struct omap_dss_device *dssdev = td->dssdev;
dev_err(&dssdev->dev, "TE not received for 250ms!\n");
atomic_set(&td->do_update, 0);
dsi_bus_unlock();
}
static int taal_update(struct omap_dss_device *dssdev, static int taal_update(struct omap_dss_device *dssdev,
u16 x, u16 y, u16 w, u16 h) u16 x, u16 y, u16 w, u16 h)
{ {
struct taal_data *td = dev_get_drvdata(&dssdev->dev); struct taal_data *td = dev_get_drvdata(&dssdev->dev);
struct nokia_dsi_panel_data *panel_data = get_panel_data(dssdev);
int r; int r;
dev_dbg(&dssdev->dev, "update %d, %d, %d x %d\n", x, y, w, h); dev_dbg(&dssdev->dev, "update %d, %d, %d x %d\n", x, y, w, h);
...@@ -853,7 +1135,7 @@ static int taal_update(struct omap_dss_device *dssdev, ...@@ -853,7 +1135,7 @@ static int taal_update(struct omap_dss_device *dssdev,
goto err; goto err;
} }
r = omap_dsi_prepare_update(dssdev, &x, &y, &w, &h); r = omap_dsi_prepare_update(dssdev, &x, &y, &w, &h, true);
if (r) if (r)
goto err; goto err;
...@@ -861,10 +1143,21 @@ static int taal_update(struct omap_dss_device *dssdev, ...@@ -861,10 +1143,21 @@ static int taal_update(struct omap_dss_device *dssdev,
if (r) if (r)
goto err; goto err;
r = omap_dsi_update(dssdev, TCH, x, y, w, h, if (td->te_enabled && panel_data->use_ext_te) {
taal_framedone_cb, dssdev); td->update_region.x = x;
if (r) td->update_region.y = y;
goto err; td->update_region.w = w;
td->update_region.h = h;
barrier();
schedule_delayed_work(&td->te_timeout_work,
msecs_to_jiffies(250));
atomic_set(&td->do_update, 1);
} else {
r = omap_dsi_update(dssdev, TCH, x, y, w, h,
taal_framedone_cb, dssdev);
if (r)
goto err;
}
/* note: no bus_unlock here. unlock is in framedone_cb */ /* note: no bus_unlock here. unlock is in framedone_cb */
mutex_unlock(&td->lock); mutex_unlock(&td->lock);
...@@ -894,20 +1187,19 @@ static int taal_sync(struct omap_dss_device *dssdev) ...@@ -894,20 +1187,19 @@ static int taal_sync(struct omap_dss_device *dssdev)
static int _taal_enable_te(struct omap_dss_device *dssdev, bool enable) static int _taal_enable_te(struct omap_dss_device *dssdev, bool enable)
{ {
struct taal_data *td = dev_get_drvdata(&dssdev->dev); struct taal_data *td = dev_get_drvdata(&dssdev->dev);
struct nokia_dsi_panel_data *panel_data = get_panel_data(dssdev);
int r; int r;
td->te_enabled = enable;
if (enable) if (enable)
r = taal_dcs_write_1(DCS_TEAR_ON, 0); r = taal_dcs_write_1(DCS_TEAR_ON, 0);
else else
r = taal_dcs_write_0(DCS_TEAR_OFF); r = taal_dcs_write_0(DCS_TEAR_OFF);
omapdss_dsi_enable_te(dssdev, enable); if (!panel_data->use_ext_te)
omapdss_dsi_enable_te(dssdev, enable);
/* XXX for some reason, DSI TE breaks if we don't wait here. if (td->panel_config->sleep.enable_te)
* Panel bug? Needs more studying */ msleep(td->panel_config->sleep.enable_te);
msleep(100);
return r; return r;
} }
...@@ -918,10 +1210,26 @@ static int taal_enable_te(struct omap_dss_device *dssdev, bool enable) ...@@ -918,10 +1210,26 @@ static int taal_enable_te(struct omap_dss_device *dssdev, bool enable)
int r; int r;
mutex_lock(&td->lock); mutex_lock(&td->lock);
if (td->te_enabled == enable)
goto end;
dsi_bus_lock(); dsi_bus_lock();
r = _taal_enable_te(dssdev, enable); if (td->enabled) {
r = _taal_enable_te(dssdev, enable);
if (r)
goto err;
}
td->te_enabled = enable;
dsi_bus_unlock();
end:
mutex_unlock(&td->lock);
return 0;
err:
dsi_bus_unlock(); dsi_bus_unlock();
mutex_unlock(&td->lock); mutex_unlock(&td->lock);
...@@ -948,6 +1256,10 @@ static int taal_rotate(struct omap_dss_device *dssdev, u8 rotate) ...@@ -948,6 +1256,10 @@ static int taal_rotate(struct omap_dss_device *dssdev, u8 rotate)
dev_dbg(&dssdev->dev, "rotate %d\n", rotate); dev_dbg(&dssdev->dev, "rotate %d\n", rotate);
mutex_lock(&td->lock); mutex_lock(&td->lock);
if (td->rotate == rotate)
goto end;
dsi_bus_lock(); dsi_bus_lock();
if (td->enabled) { if (td->enabled) {
...@@ -959,6 +1271,7 @@ static int taal_rotate(struct omap_dss_device *dssdev, u8 rotate) ...@@ -959,6 +1271,7 @@ static int taal_rotate(struct omap_dss_device *dssdev, u8 rotate)
td->rotate = rotate; td->rotate = rotate;
dsi_bus_unlock(); dsi_bus_unlock();
end:
mutex_unlock(&td->lock); mutex_unlock(&td->lock);
return 0; return 0;
err: err:
...@@ -987,6 +1300,10 @@ static int taal_mirror(struct omap_dss_device *dssdev, bool enable) ...@@ -987,6 +1300,10 @@ static int taal_mirror(struct omap_dss_device *dssdev, bool enable)
dev_dbg(&dssdev->dev, "mirror %d\n", enable); dev_dbg(&dssdev->dev, "mirror %d\n", enable);
mutex_lock(&td->lock); mutex_lock(&td->lock);
if (td->mirror == enable)
goto end;
dsi_bus_lock(); dsi_bus_lock();
if (td->enabled) { if (td->enabled) {
r = taal_set_addr_mode(td->rotate, enable); r = taal_set_addr_mode(td->rotate, enable);
...@@ -997,6 +1314,7 @@ static int taal_mirror(struct omap_dss_device *dssdev, bool enable) ...@@ -997,6 +1314,7 @@ static int taal_mirror(struct omap_dss_device *dssdev, bool enable)
td->mirror = enable; td->mirror = enable;
dsi_bus_unlock(); dsi_bus_unlock();
end:
mutex_unlock(&td->lock); mutex_unlock(&td->lock);
return 0; return 0;
err: err:
...@@ -1024,23 +1342,30 @@ static int taal_run_test(struct omap_dss_device *dssdev, int test_num) ...@@ -1024,23 +1342,30 @@ static int taal_run_test(struct omap_dss_device *dssdev, int test_num)
int r; int r;
mutex_lock(&td->lock); mutex_lock(&td->lock);
if (!td->enabled) {
r = -ENODEV;
goto err1;
}
dsi_bus_lock(); dsi_bus_lock();
r = taal_dcs_read_1(DCS_GET_ID1, &id1); r = taal_dcs_read_1(DCS_GET_ID1, &id1);
if (r) if (r)
goto err; goto err2;
r = taal_dcs_read_1(DCS_GET_ID2, &id2); r = taal_dcs_read_1(DCS_GET_ID2, &id2);
if (r) if (r)
goto err; goto err2;
r = taal_dcs_read_1(DCS_GET_ID3, &id3); r = taal_dcs_read_1(DCS_GET_ID3, &id3);
if (r) if (r)
goto err; goto err2;
dsi_bus_unlock(); dsi_bus_unlock();
mutex_unlock(&td->lock); mutex_unlock(&td->lock);
return 0; return 0;
err: err2:
dsi_bus_unlock(); dsi_bus_unlock();
err1:
mutex_unlock(&td->lock); mutex_unlock(&td->lock);
return r; return r;
} }
...@@ -1128,6 +1453,7 @@ static void taal_esd_work(struct work_struct *work) ...@@ -1128,6 +1453,7 @@ static void taal_esd_work(struct work_struct *work)
struct taal_data *td = container_of(work, struct taal_data, struct taal_data *td = container_of(work, struct taal_data,
esd_work.work); esd_work.work);
struct omap_dss_device *dssdev = td->dssdev; struct omap_dss_device *dssdev = td->dssdev;
struct nokia_dsi_panel_data *panel_data = get_panel_data(dssdev);
u8 state1, state2; u8 state1, state2;
int r; int r;
...@@ -1168,7 +1494,7 @@ static void taal_esd_work(struct work_struct *work) ...@@ -1168,7 +1494,7 @@ static void taal_esd_work(struct work_struct *work)
} }
/* Self-diagnostics result is also shown on TE GPIO line. We need /* Self-diagnostics result is also shown on TE GPIO line. We need
* to re-enable TE after self diagnostics */ * to re-enable TE after self diagnostics */
if (td->use_ext_te && td->te_enabled) { if (td->te_enabled && panel_data->use_ext_te) {
r = taal_dcs_write_1(DCS_TEAR_ON, 0); r = taal_dcs_write_1(DCS_TEAR_ON, 0);
if (r) if (r)
goto err; goto err;
...@@ -1184,6 +1510,7 @@ static void taal_esd_work(struct work_struct *work) ...@@ -1184,6 +1510,7 @@ static void taal_esd_work(struct work_struct *work)
dev_err(&dssdev->dev, "performing LCD reset\n"); dev_err(&dssdev->dev, "performing LCD reset\n");
taal_power_off(dssdev); taal_power_off(dssdev);
taal_hw_reset(dssdev);
taal_power_on(dssdev); taal_power_on(dssdev);
dsi_bus_unlock(); dsi_bus_unlock();
......
...@@ -73,8 +73,12 @@ static void toppoly_tdo_panel_power_off(struct omap_dss_device *dssdev) ...@@ -73,8 +73,12 @@ static void toppoly_tdo_panel_power_off(struct omap_dss_device *dssdev)
static int toppoly_tdo_panel_probe(struct omap_dss_device *dssdev) static int toppoly_tdo_panel_probe(struct omap_dss_device *dssdev)
{ {
dssdev->panel.config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS | dssdev->panel.config = OMAP_DSS_LCD_TFT |
OMAP_DSS_LCD_IHS; OMAP_DSS_LCD_IVS |
OMAP_DSS_LCD_IHS |
OMAP_DSS_LCD_IPC |
OMAP_DSS_LCD_ONOFF;
dssdev->panel.timings = toppoly_tdo_panel_timings; dssdev->panel.timings = toppoly_tdo_panel_timings;
return 0; return 0;
......
...@@ -31,6 +31,7 @@ ...@@ -31,6 +31,7 @@
#include <linux/seq_file.h> #include <linux/seq_file.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/workqueue.h> #include <linux/workqueue.h>
#include <linux/hardirq.h>
#include <plat/sram.h> #include <plat/sram.h>
#include <plat/clock.h> #include <plat/clock.h>
...@@ -335,7 +336,7 @@ void dispc_save_context(void) ...@@ -335,7 +336,7 @@ void dispc_save_context(void)
void dispc_restore_context(void) void dispc_restore_context(void)
{ {
RR(SYSCONFIG); RR(SYSCONFIG);
RR(IRQENABLE); /*RR(IRQENABLE);*/
/*RR(CONTROL);*/ /*RR(CONTROL);*/
RR(CONFIG); RR(CONFIG);
RR(DEFAULT_COLOR0); RR(DEFAULT_COLOR0);
...@@ -472,6 +473,15 @@ void dispc_restore_context(void) ...@@ -472,6 +473,15 @@ void dispc_restore_context(void)
/* enable last, because LCD & DIGIT enable are here */ /* enable last, because LCD & DIGIT enable are here */
RR(CONTROL); RR(CONTROL);
/* clear spurious SYNC_LOST_DIGIT interrupts */
dispc_write_reg(DISPC_IRQSTATUS, DISPC_IRQ_SYNC_LOST_DIGIT);
/*
* enable last so IRQs won't trigger before
* the context is fully restored
*/
RR(IRQENABLE);
} }
#undef SR #undef SR
...@@ -3019,7 +3029,7 @@ void dispc_fake_vsync_irq(void) ...@@ -3019,7 +3029,7 @@ void dispc_fake_vsync_irq(void)
u32 irqstatus = DISPC_IRQ_VSYNC; u32 irqstatus = DISPC_IRQ_VSYNC;
int i; int i;
local_irq_disable(); WARN_ON(!in_interrupt());
for (i = 0; i < DISPC_MAX_NR_ISRS; i++) { for (i = 0; i < DISPC_MAX_NR_ISRS; i++) {
struct omap_dispc_isr_data *isr_data; struct omap_dispc_isr_data *isr_data;
...@@ -3031,8 +3041,6 @@ void dispc_fake_vsync_irq(void) ...@@ -3031,8 +3041,6 @@ void dispc_fake_vsync_irq(void)
if (isr_data->mask & irqstatus) if (isr_data->mask & irqstatus)
isr_data->isr(isr_data->arg, irqstatus); isr_data->isr(isr_data->arg, irqstatus);
} }
local_irq_enable();
} }
#endif #endif
......
...@@ -82,6 +82,9 @@ static ssize_t display_upd_mode_store(struct device *dev, ...@@ -82,6 +82,9 @@ static ssize_t display_upd_mode_store(struct device *dev,
int val, r; int val, r;
enum omap_dss_update_mode mode; enum omap_dss_update_mode mode;
if (!dssdev->driver->set_update_mode)
return -EINVAL;
val = simple_strtoul(buf, NULL, 10); val = simple_strtoul(buf, NULL, 10);
switch (val) { switch (val) {
...@@ -343,7 +346,6 @@ int omapdss_default_get_recommended_bpp(struct omap_dss_device *dssdev) ...@@ -343,7 +346,6 @@ int omapdss_default_get_recommended_bpp(struct omap_dss_device *dssdev)
case OMAP_DISPLAY_TYPE_VENC: case OMAP_DISPLAY_TYPE_VENC:
case OMAP_DISPLAY_TYPE_SDI: case OMAP_DISPLAY_TYPE_SDI:
return 24; return 24;
return 24;
default: default:
BUG(); BUG();
} }
......
...@@ -165,6 +165,14 @@ struct dsi_reg { u16 idx; }; ...@@ -165,6 +165,14 @@ struct dsi_reg { u16 idx; };
#define DSI_CIO_IRQ_ERRCONTENTIONLP1_3 (1 << 25) #define DSI_CIO_IRQ_ERRCONTENTIONLP1_3 (1 << 25)
#define DSI_CIO_IRQ_ULPSACTIVENOT_ALL0 (1 << 30) #define DSI_CIO_IRQ_ULPSACTIVENOT_ALL0 (1 << 30)
#define DSI_CIO_IRQ_ULPSACTIVENOT_ALL1 (1 << 31) #define DSI_CIO_IRQ_ULPSACTIVENOT_ALL1 (1 << 31)
#define DSI_CIO_IRQ_ERROR_MASK \
(DSI_CIO_IRQ_ERRSYNCESC1 | DSI_CIO_IRQ_ERRSYNCESC2 | \
DSI_CIO_IRQ_ERRSYNCESC3 | DSI_CIO_IRQ_ERRESC1 | DSI_CIO_IRQ_ERRESC2 | \
DSI_CIO_IRQ_ERRESC3 | DSI_CIO_IRQ_ERRCONTROL1 | \
DSI_CIO_IRQ_ERRCONTROL2 | DSI_CIO_IRQ_ERRCONTROL3 | \
DSI_CIO_IRQ_ERRCONTENTIONLP0_1 | DSI_CIO_IRQ_ERRCONTENTIONLP1_1 | \
DSI_CIO_IRQ_ERRCONTENTIONLP0_2 | DSI_CIO_IRQ_ERRCONTENTIONLP1_2 | \
DSI_CIO_IRQ_ERRCONTENTIONLP0_3 | DSI_CIO_IRQ_ERRCONTENTIONLP1_3)
#define DSI_DT_DCS_SHORT_WRITE_0 0x05 #define DSI_DT_DCS_SHORT_WRITE_0 0x05
#define DSI_DT_DCS_SHORT_WRITE_1 0x15 #define DSI_DT_DCS_SHORT_WRITE_1 0x15
...@@ -232,13 +240,15 @@ static struct ...@@ -232,13 +240,15 @@ static struct
unsigned pll_locked; unsigned pll_locked;
struct completion bta_completion; struct completion bta_completion;
void (*bta_callback)(void);
int update_channel; int update_channel;
struct dsi_update_region update_region; struct dsi_update_region update_region;
bool te_enabled; bool te_enabled;
struct work_struct framedone_work; struct workqueue_struct *workqueue;
void (*framedone_callback)(int, void *); void (*framedone_callback)(int, void *);
void *framedone_data; void *framedone_data;
...@@ -509,9 +519,13 @@ void dsi_irq_handler(void) ...@@ -509,9 +519,13 @@ void dsi_irq_handler(void)
dss_collect_irq_stats(vcstatus, dsi.irq_stats.vc_irqs[i]); dss_collect_irq_stats(vcstatus, dsi.irq_stats.vc_irqs[i]);
#endif #endif
if (vcstatus & DSI_VC_IRQ_BTA) if (vcstatus & DSI_VC_IRQ_BTA) {
complete(&dsi.bta_completion); complete(&dsi.bta_completion);
if (dsi.bta_callback)
dsi.bta_callback();
}
if (vcstatus & DSI_VC_IRQ_ERROR_MASK) { if (vcstatus & DSI_VC_IRQ_ERROR_MASK) {
DSSERR("DSI VC(%d) error, vc irqstatus %x\n", DSSERR("DSI VC(%d) error, vc irqstatus %x\n",
i, vcstatus); i, vcstatus);
...@@ -536,8 +550,12 @@ void dsi_irq_handler(void) ...@@ -536,8 +550,12 @@ void dsi_irq_handler(void)
/* flush posted write */ /* flush posted write */
dsi_read_reg(DSI_COMPLEXIO_IRQ_STATUS); dsi_read_reg(DSI_COMPLEXIO_IRQ_STATUS);
DSSERR("DSI CIO error, cio irqstatus %x\n", ciostatus); if (ciostatus & DSI_CIO_IRQ_ERROR_MASK) {
print_irq_status_cio(ciostatus); DSSERR("DSI CIO error, cio irqstatus %x\n", ciostatus);
print_irq_status_cio(ciostatus);
} else if (debug_irq) {
print_irq_status_cio(ciostatus);
}
} }
dsi_write_reg(DSI_IRQSTATUS, irqstatus & ~DSI_IRQ_CHANNEL_MASK); dsi_write_reg(DSI_IRQSTATUS, irqstatus & ~DSI_IRQ_CHANNEL_MASK);
...@@ -584,11 +602,8 @@ static void _dsi_initialize_irq(void) ...@@ -584,11 +602,8 @@ static void _dsi_initialize_irq(void)
for (i = 0; i < 4; ++i) for (i = 0; i < 4; ++i)
dsi_write_reg(DSI_VC_IRQENABLE(i), l); dsi_write_reg(DSI_VC_IRQENABLE(i), l);
/* XXX zonda responds incorrectly, causing control error: l = DSI_CIO_IRQ_ERROR_MASK;
Exit from LP-ESC mode to LP11 uses wrong transition states on the dsi_write_reg(DSI_COMPLEXIO_IRQ_ENABLE, l);
data lines LP0 and LN0. */
dsi_write_reg(DSI_COMPLEXIO_IRQ_ENABLE,
-1 & (~DSI_CIO_IRQ_ERRCONTROL2));
} }
static u32 dsi_get_errors(void) static u32 dsi_get_errors(void)
...@@ -1098,6 +1113,7 @@ int dsi_pll_init(struct omap_dss_device *dssdev, bool enable_hsclk, ...@@ -1098,6 +1113,7 @@ int dsi_pll_init(struct omap_dss_device *dssdev, bool enable_hsclk,
if (wait_for_bit_change(DSI_PLL_STATUS, 0, 1) != 1) { if (wait_for_bit_change(DSI_PLL_STATUS, 0, 1) != 1) {
DSSERR("PLL not coming out of reset.\n"); DSSERR("PLL not coming out of reset.\n");
r = -ENODEV; r = -ENODEV;
dispc_pck_free_enable(0);
goto err1; goto err1;
} }
...@@ -1740,42 +1756,52 @@ static void dsi_vc_initial_config(int channel) ...@@ -1740,42 +1756,52 @@ static void dsi_vc_initial_config(int channel)
dsi.vc[channel].mode = DSI_VC_MODE_L4; dsi.vc[channel].mode = DSI_VC_MODE_L4;
} }
static void dsi_vc_config_l4(int channel) static int dsi_vc_config_l4(int channel)
{ {
if (dsi.vc[channel].mode == DSI_VC_MODE_L4) if (dsi.vc[channel].mode == DSI_VC_MODE_L4)
return; return 0;
DSSDBGF("%d", channel); DSSDBGF("%d", channel);
dsi_vc_enable(channel, 0); dsi_vc_enable(channel, 0);
if (REG_GET(DSI_VC_CTRL(channel), 15, 15)) /* VC_BUSY */ /* VC_BUSY */
if (wait_for_bit_change(DSI_VC_CTRL(channel), 15, 0) != 0) {
DSSERR("vc(%d) busy when trying to config for L4\n", channel); DSSERR("vc(%d) busy when trying to config for L4\n", channel);
return -EIO;
}
REG_FLD_MOD(DSI_VC_CTRL(channel), 0, 1, 1); /* SOURCE, 0 = L4 */ REG_FLD_MOD(DSI_VC_CTRL(channel), 0, 1, 1); /* SOURCE, 0 = L4 */
dsi_vc_enable(channel, 1); dsi_vc_enable(channel, 1);
dsi.vc[channel].mode = DSI_VC_MODE_L4; dsi.vc[channel].mode = DSI_VC_MODE_L4;
return 0;
} }
static void dsi_vc_config_vp(int channel) static int dsi_vc_config_vp(int channel)
{ {
if (dsi.vc[channel].mode == DSI_VC_MODE_VP) if (dsi.vc[channel].mode == DSI_VC_MODE_VP)
return; return 0;
DSSDBGF("%d", channel); DSSDBGF("%d", channel);
dsi_vc_enable(channel, 0); dsi_vc_enable(channel, 0);
if (REG_GET(DSI_VC_CTRL(channel), 15, 15)) /* VC_BUSY */ /* VC_BUSY */
if (wait_for_bit_change(DSI_VC_CTRL(channel), 15, 0) != 0) {
DSSERR("vc(%d) busy when trying to config for VP\n", channel); DSSERR("vc(%d) busy when trying to config for VP\n", channel);
return -EIO;
}
REG_FLD_MOD(DSI_VC_CTRL(channel), 1, 1, 1); /* SOURCE, 1 = video port */ REG_FLD_MOD(DSI_VC_CTRL(channel), 1, 1, 1); /* SOURCE, 1 = video port */
dsi_vc_enable(channel, 1); dsi_vc_enable(channel, 1);
dsi.vc[channel].mode = DSI_VC_MODE_VP; dsi.vc[channel].mode = DSI_VC_MODE_VP;
return 0;
} }
...@@ -1854,19 +1880,19 @@ static u16 dsi_vc_flush_receive_data(int channel) ...@@ -1854,19 +1880,19 @@ static u16 dsi_vc_flush_receive_data(int channel)
u32 val; u32 val;
u8 dt; u8 dt;
val = dsi_read_reg(DSI_VC_SHORT_PACKET_HEADER(channel)); val = dsi_read_reg(DSI_VC_SHORT_PACKET_HEADER(channel));
DSSDBG("\trawval %#08x\n", val); DSSERR("\trawval %#08x\n", val);
dt = FLD_GET(val, 5, 0); dt = FLD_GET(val, 5, 0);
if (dt == DSI_DT_RX_ACK_WITH_ERR) { if (dt == DSI_DT_RX_ACK_WITH_ERR) {
u16 err = FLD_GET(val, 23, 8); u16 err = FLD_GET(val, 23, 8);
dsi_show_rx_ack_with_err(err); dsi_show_rx_ack_with_err(err);
} else if (dt == DSI_DT_RX_SHORT_READ_1) { } else if (dt == DSI_DT_RX_SHORT_READ_1) {
DSSDBG("\tDCS short response, 1 byte: %#x\n", DSSERR("\tDCS short response, 1 byte: %#x\n",
FLD_GET(val, 23, 8)); FLD_GET(val, 23, 8));
} else if (dt == DSI_DT_RX_SHORT_READ_2) { } else if (dt == DSI_DT_RX_SHORT_READ_2) {
DSSDBG("\tDCS short response, 2 byte: %#x\n", DSSERR("\tDCS short response, 2 byte: %#x\n",
FLD_GET(val, 23, 8)); FLD_GET(val, 23, 8));
} else if (dt == DSI_DT_RX_DCS_LONG_READ) { } else if (dt == DSI_DT_RX_DCS_LONG_READ) {
DSSDBG("\tDCS long response, len %d\n", DSSERR("\tDCS long response, len %d\n",
FLD_GET(val, 23, 8)); FLD_GET(val, 23, 8));
dsi_vc_flush_long_data(channel); dsi_vc_flush_long_data(channel);
} else { } else {
...@@ -2087,6 +2113,13 @@ int dsi_vc_dcs_write(int channel, u8 *data, int len) ...@@ -2087,6 +2113,13 @@ int dsi_vc_dcs_write(int channel, u8 *data, int len)
if (r) if (r)
goto err; goto err;
if (REG_GET(DSI_VC_CTRL(channel), 20, 20)) { /* RX_FIFO_NOT_EMPTY */
DSSERR("rx fifo not empty after write, dumping data:\n");
dsi_vc_flush_receive_data(channel);
r = -EIO;
goto err;
}
return 0; return 0;
err: err:
DSSERR("dsi_vc_dcs_write(ch %d, cmd 0x%02x, len %d) failed\n", DSSERR("dsi_vc_dcs_write(ch %d, cmd 0x%02x, len %d) failed\n",
...@@ -2233,11 +2266,12 @@ int dsi_vc_dcs_read_1(int channel, u8 dcs_cmd, u8 *data) ...@@ -2233,11 +2266,12 @@ int dsi_vc_dcs_read_1(int channel, u8 dcs_cmd, u8 *data)
} }
EXPORT_SYMBOL(dsi_vc_dcs_read_1); EXPORT_SYMBOL(dsi_vc_dcs_read_1);
int dsi_vc_dcs_read_2(int channel, u8 dcs_cmd, u16 *data) int dsi_vc_dcs_read_2(int channel, u8 dcs_cmd, u8 *data1, u8 *data2)
{ {
u8 buf[2];
int r; int r;
r = dsi_vc_dcs_read(channel, dcs_cmd, (u8 *)data, 2); r = dsi_vc_dcs_read(channel, dcs_cmd, buf, 2);
if (r < 0) if (r < 0)
return r; return r;
...@@ -2245,231 +2279,122 @@ int dsi_vc_dcs_read_2(int channel, u8 dcs_cmd, u16 *data) ...@@ -2245,231 +2279,122 @@ int dsi_vc_dcs_read_2(int channel, u8 dcs_cmd, u16 *data)
if (r != 2) if (r != 2)
return -EIO; return -EIO;
*data1 = buf[0];
*data2 = buf[1];
return 0; return 0;
} }
EXPORT_SYMBOL(dsi_vc_dcs_read_2); EXPORT_SYMBOL(dsi_vc_dcs_read_2);
int dsi_vc_set_max_rx_packet_size(int channel, u16 len) int dsi_vc_set_max_rx_packet_size(int channel, u16 len)
{ {
int r; return dsi_vc_send_short(channel, DSI_DT_SET_MAX_RET_PKG_SIZE,
r = dsi_vc_send_short(channel, DSI_DT_SET_MAX_RET_PKG_SIZE,
len, 0); len, 0);
if (r)
return r;
r = dsi_vc_send_bta_sync(channel);
return r;
} }
EXPORT_SYMBOL(dsi_vc_set_max_rx_packet_size); EXPORT_SYMBOL(dsi_vc_set_max_rx_packet_size);
static void dsi_set_lp_rx_timeout(unsigned long ns) static void dsi_set_lp_rx_timeout(unsigned ticks, bool x4, bool x16)
{ {
u32 r;
unsigned x4, x16;
unsigned long fck; unsigned long fck;
unsigned long ticks; unsigned long total_ticks;
u32 r;
/* ticks in DSI_FCK */ BUG_ON(ticks > 0x1fff);
/* ticks in DSI_FCK */
fck = dsi_fclk_rate(); fck = dsi_fclk_rate();
ticks = (fck / 1000 / 1000) * ns / 1000;
x4 = 0;
x16 = 0;
if (ticks > 0x1fff) {
ticks = (fck / 1000 / 1000) * ns / 1000 / 4;
x4 = 1;
x16 = 0;
}
if (ticks > 0x1fff) {
ticks = (fck / 1000 / 1000) * ns / 1000 / 16;
x4 = 0;
x16 = 1;
}
if (ticks > 0x1fff) {
ticks = (fck / 1000 / 1000) * ns / 1000 / (4 * 16);
x4 = 1;
x16 = 1;
}
if (ticks > 0x1fff) {
DSSWARN("LP_TX_TO over limit, setting it to max\n");
ticks = 0x1fff;
x4 = 1;
x16 = 1;
}
r = dsi_read_reg(DSI_TIMING2); r = dsi_read_reg(DSI_TIMING2);
r = FLD_MOD(r, 1, 15, 15); /* LP_RX_TO */ r = FLD_MOD(r, 1, 15, 15); /* LP_RX_TO */
r = FLD_MOD(r, x16, 14, 14); /* LP_RX_TO_X16 */ r = FLD_MOD(r, x16 ? 1 : 0, 14, 14); /* LP_RX_TO_X16 */
r = FLD_MOD(r, x4, 13, 13); /* LP_RX_TO_X4 */ r = FLD_MOD(r, x4 ? 1 : 0, 13, 13); /* LP_RX_TO_X4 */
r = FLD_MOD(r, ticks, 12, 0); /* LP_RX_COUNTER */ r = FLD_MOD(r, ticks, 12, 0); /* LP_RX_COUNTER */
dsi_write_reg(DSI_TIMING2, r); dsi_write_reg(DSI_TIMING2, r);
DSSDBG("LP_RX_TO %lu ns (%#lx ticks%s%s)\n", total_ticks = ticks * (x16 ? 16 : 1) * (x4 ? 4 : 1);
(ticks * (x16 ? 16 : 1) * (x4 ? 4 : 1) * 1000) /
(fck / 1000 / 1000), DSSDBG("LP_RX_TO %lu ticks (%#x%s%s) = %lu ns\n",
ticks, x4 ? " x4" : "", x16 ? " x16" : ""); total_ticks,
ticks, x4 ? " x4" : "", x16 ? " x16" : "",
(total_ticks * 1000) / (fck / 1000 / 1000));
} }
static void dsi_set_ta_timeout(unsigned long ns) static void dsi_set_ta_timeout(unsigned ticks, bool x8, bool x16)
{ {
u32 r;
unsigned x8, x16;
unsigned long fck; unsigned long fck;
unsigned long ticks; unsigned long total_ticks;
u32 r;
BUG_ON(ticks > 0x1fff);
/* ticks in DSI_FCK */ /* ticks in DSI_FCK */
fck = dsi_fclk_rate(); fck = dsi_fclk_rate();
ticks = (fck / 1000 / 1000) * ns / 1000;
x8 = 0;
x16 = 0;
if (ticks > 0x1fff) {
ticks = (fck / 1000 / 1000) * ns / 1000 / 8;
x8 = 1;
x16 = 0;
}
if (ticks > 0x1fff) {
ticks = (fck / 1000 / 1000) * ns / 1000 / 16;
x8 = 0;
x16 = 1;
}
if (ticks > 0x1fff) {
ticks = (fck / 1000 / 1000) * ns / 1000 / (8 * 16);
x8 = 1;
x16 = 1;
}
if (ticks > 0x1fff) {
DSSWARN("TA_TO over limit, setting it to max\n");
ticks = 0x1fff;
x8 = 1;
x16 = 1;
}
r = dsi_read_reg(DSI_TIMING1); r = dsi_read_reg(DSI_TIMING1);
r = FLD_MOD(r, 1, 31, 31); /* TA_TO */ r = FLD_MOD(r, 1, 31, 31); /* TA_TO */
r = FLD_MOD(r, x16, 30, 30); /* TA_TO_X16 */ r = FLD_MOD(r, x16 ? 1 : 0, 30, 30); /* TA_TO_X16 */
r = FLD_MOD(r, x8, 29, 29); /* TA_TO_X8 */ r = FLD_MOD(r, x8 ? 1 : 0, 29, 29); /* TA_TO_X8 */
r = FLD_MOD(r, ticks, 28, 16); /* TA_TO_COUNTER */ r = FLD_MOD(r, ticks, 28, 16); /* TA_TO_COUNTER */
dsi_write_reg(DSI_TIMING1, r); dsi_write_reg(DSI_TIMING1, r);
DSSDBG("TA_TO %lu ns (%#lx ticks%s%s)\n", total_ticks = ticks * (x16 ? 16 : 1) * (x8 ? 8 : 1);
(ticks * (x16 ? 16 : 1) * (x8 ? 8 : 1) * 1000) /
(fck / 1000 / 1000), DSSDBG("TA_TO %lu ticks (%#x%s%s) = %lu ns\n",
ticks, x8 ? " x8" : "", x16 ? " x16" : ""); total_ticks,
ticks, x8 ? " x8" : "", x16 ? " x16" : "",
(total_ticks * 1000) / (fck / 1000 / 1000));
} }
static void dsi_set_stop_state_counter(unsigned long ns) static void dsi_set_stop_state_counter(unsigned ticks, bool x4, bool x16)
{ {
u32 r;
unsigned x4, x16;
unsigned long fck; unsigned long fck;
unsigned long ticks; unsigned long total_ticks;
u32 r;
/* ticks in DSI_FCK */ BUG_ON(ticks > 0x1fff);
/* ticks in DSI_FCK */
fck = dsi_fclk_rate(); fck = dsi_fclk_rate();
ticks = (fck / 1000 / 1000) * ns / 1000;
x4 = 0;
x16 = 0;
if (ticks > 0x1fff) {
ticks = (fck / 1000 / 1000) * ns / 1000 / 4;
x4 = 1;
x16 = 0;
}
if (ticks > 0x1fff) {
ticks = (fck / 1000 / 1000) * ns / 1000 / 16;
x4 = 0;
x16 = 1;
}
if (ticks > 0x1fff) {
ticks = (fck / 1000 / 1000) * ns / 1000 / (4 * 16);
x4 = 1;
x16 = 1;
}
if (ticks > 0x1fff) {
DSSWARN("STOP_STATE_COUNTER_IO over limit, "
"setting it to max\n");
ticks = 0x1fff;
x4 = 1;
x16 = 1;
}
r = dsi_read_reg(DSI_TIMING1); r = dsi_read_reg(DSI_TIMING1);
r = FLD_MOD(r, 1, 15, 15); /* FORCE_TX_STOP_MODE_IO */ r = FLD_MOD(r, 1, 15, 15); /* FORCE_TX_STOP_MODE_IO */
r = FLD_MOD(r, x16, 14, 14); /* STOP_STATE_X16_IO */ r = FLD_MOD(r, x16 ? 1 : 0, 14, 14); /* STOP_STATE_X16_IO */
r = FLD_MOD(r, x4, 13, 13); /* STOP_STATE_X4_IO */ r = FLD_MOD(r, x4 ? 1 : 0, 13, 13); /* STOP_STATE_X4_IO */
r = FLD_MOD(r, ticks, 12, 0); /* STOP_STATE_COUNTER_IO */ r = FLD_MOD(r, ticks, 12, 0); /* STOP_STATE_COUNTER_IO */
dsi_write_reg(DSI_TIMING1, r); dsi_write_reg(DSI_TIMING1, r);
DSSDBG("STOP_STATE_COUNTER %lu ns (%#lx ticks%s%s)\n", total_ticks = ticks * (x16 ? 16 : 1) * (x4 ? 4 : 1);
(ticks * (x16 ? 16 : 1) * (x4 ? 4 : 1) * 1000) /
(fck / 1000 / 1000), DSSDBG("STOP_STATE_COUNTER %lu ticks (%#x%s%s) = %lu ns\n",
ticks, x4 ? " x4" : "", x16 ? " x16" : ""); total_ticks,
ticks, x4 ? " x4" : "", x16 ? " x16" : "",
(total_ticks * 1000) / (fck / 1000 / 1000));
} }
static void dsi_set_hs_tx_timeout(unsigned long ns) static void dsi_set_hs_tx_timeout(unsigned ticks, bool x4, bool x16)
{ {
u32 r;
unsigned x4, x16;
unsigned long fck; unsigned long fck;
unsigned long ticks; unsigned long total_ticks;
u32 r;
/* ticks in TxByteClkHS */ BUG_ON(ticks > 0x1fff);
/* ticks in TxByteClkHS */
fck = dsi_get_txbyteclkhs(); fck = dsi_get_txbyteclkhs();
ticks = (fck / 1000 / 1000) * ns / 1000;
x4 = 0;
x16 = 0;
if (ticks > 0x1fff) {
ticks = (fck / 1000 / 1000) * ns / 1000 / 4;
x4 = 1;
x16 = 0;
}
if (ticks > 0x1fff) {
ticks = (fck / 1000 / 1000) * ns / 1000 / 16;
x4 = 0;
x16 = 1;
}
if (ticks > 0x1fff) {
ticks = (fck / 1000 / 1000) * ns / 1000 / (4 * 16);
x4 = 1;
x16 = 1;
}
if (ticks > 0x1fff) {
DSSWARN("HS_TX_TO over limit, setting it to max\n");
ticks = 0x1fff;
x4 = 1;
x16 = 1;
}
r = dsi_read_reg(DSI_TIMING2); r = dsi_read_reg(DSI_TIMING2);
r = FLD_MOD(r, 1, 31, 31); /* HS_TX_TO */ r = FLD_MOD(r, 1, 31, 31); /* HS_TX_TO */
r = FLD_MOD(r, x16, 30, 30); /* HS_TX_TO_X16 */ r = FLD_MOD(r, x16 ? 1 : 0, 30, 30); /* HS_TX_TO_X16 */
r = FLD_MOD(r, x4, 29, 29); /* HS_TX_TO_X8 (4 really) */ r = FLD_MOD(r, x4 ? 1 : 0, 29, 29); /* HS_TX_TO_X8 (4 really) */
r = FLD_MOD(r, ticks, 28, 16); /* HS_TX_TO_COUNTER */ r = FLD_MOD(r, ticks, 28, 16); /* HS_TX_TO_COUNTER */
dsi_write_reg(DSI_TIMING2, r); dsi_write_reg(DSI_TIMING2, r);
DSSDBG("HS_TX_TO %lu ns (%#lx ticks%s%s)\n", total_ticks = ticks * (x16 ? 16 : 1) * (x4 ? 4 : 1);
(ticks * (x16 ? 16 : 1) * (x4 ? 4 : 1) * 1000) /
(fck / 1000 / 1000), DSSDBG("HS_TX_TO %lu ticks (%#x%s%s) = %lu ns\n",
ticks, x4 ? " x4" : "", x16 ? " x16" : ""); total_ticks,
ticks, x4 ? " x4" : "", x16 ? " x16" : "",
(total_ticks * 1000) / (fck / 1000 / 1000));
} }
static int dsi_proto_config(struct omap_dss_device *dssdev) static int dsi_proto_config(struct omap_dss_device *dssdev)
{ {
...@@ -2487,10 +2412,10 @@ static int dsi_proto_config(struct omap_dss_device *dssdev) ...@@ -2487,10 +2412,10 @@ static int dsi_proto_config(struct omap_dss_device *dssdev)
DSI_FIFO_SIZE_32); DSI_FIFO_SIZE_32);
/* XXX what values for the timeouts? */ /* XXX what values for the timeouts? */
dsi_set_stop_state_counter(1000); dsi_set_stop_state_counter(0x1000, false, false);
dsi_set_ta_timeout(6400000); dsi_set_ta_timeout(0x1fff, true, true);
dsi_set_lp_rx_timeout(48000); dsi_set_lp_rx_timeout(0x1fff, true, true);
dsi_set_hs_tx_timeout(1000000); dsi_set_hs_tx_timeout(0x1fff, true, true);
switch (dssdev->ctrl.pixel_size) { switch (dssdev->ctrl.pixel_size) {
case 16: case 16:
...@@ -2759,6 +2684,7 @@ static void dsi_update_screen_dispc(struct omap_dss_device *dssdev, ...@@ -2759,6 +2684,7 @@ static void dsi_update_screen_dispc(struct omap_dss_device *dssdev,
unsigned packet_payload; unsigned packet_payload;
unsigned packet_len; unsigned packet_len;
u32 l; u32 l;
int r;
const unsigned channel = dsi.update_channel; const unsigned channel = dsi.update_channel;
/* line buffer is 1024 x 24bits */ /* line buffer is 1024 x 24bits */
/* XXX: for some reason using full buffer size causes considerable TX /* XXX: for some reason using full buffer size causes considerable TX
...@@ -2809,8 +2735,9 @@ static void dsi_update_screen_dispc(struct omap_dss_device *dssdev, ...@@ -2809,8 +2735,9 @@ static void dsi_update_screen_dispc(struct omap_dss_device *dssdev,
dsi_perf_mark_start(); dsi_perf_mark_start();
schedule_delayed_work(&dsi.framedone_timeout_work, r = queue_delayed_work(dsi.workqueue, &dsi.framedone_timeout_work,
msecs_to_jiffies(250)); msecs_to_jiffies(250));
BUG_ON(r == 0);
dss_start_update(dssdev); dss_start_update(dssdev);
...@@ -2834,62 +2761,70 @@ static void dsi_te_timeout(unsigned long arg) ...@@ -2834,62 +2761,70 @@ static void dsi_te_timeout(unsigned long arg)
} }
#endif #endif
static void dsi_framedone_timeout_work_callback(struct work_struct *work) static void dsi_handle_framedone(int error)
{ {
int r;
const int channel = dsi.update_channel; const int channel = dsi.update_channel;
DSSERR("Framedone not received for 250ms!\n"); cancel_delayed_work(&dsi.framedone_timeout_work);
dsi_vc_disable_bta_irq(channel);
/* SIDLEMODE back to smart-idle */ /* SIDLEMODE back to smart-idle */
dispc_enable_sidle(); dispc_enable_sidle();
dsi.bta_callback = NULL;
if (dsi.te_enabled) { if (dsi.te_enabled) {
/* enable LP_RX_TO again after the TE */ /* enable LP_RX_TO again after the TE */
REG_FLD_MOD(DSI_TIMING2, 1, 15, 15); /* LP_RX_TO */ REG_FLD_MOD(DSI_TIMING2, 1, 15, 15); /* LP_RX_TO */
} }
/* Send BTA after the frame. We need this for the TE to work, as TE
* trigger is only sent for BTAs without preceding packet. Thus we need
* to BTA after the pixel packets so that next BTA will cause TE
* trigger.
*
* This is not needed when TE is not in use, but we do it anyway to
* make sure that the transfer has been completed. It would be more
* optimal, but more complex, to wait only just before starting next
* transfer. */
r = dsi_vc_send_bta_sync(channel);
if (r)
DSSERR("BTA after framedone failed\n");
/* RX_FIFO_NOT_EMPTY */ /* RX_FIFO_NOT_EMPTY */
if (REG_GET(DSI_VC_CTRL(channel), 20, 20)) { if (REG_GET(DSI_VC_CTRL(channel), 20, 20)) {
DSSERR("Received error during frame transfer:\n"); DSSERR("Received error during frame transfer:\n");
dsi_vc_flush_receive_data(channel); dsi_vc_flush_receive_data(channel);
if (!error)
error = -EIO;
} }
dsi.framedone_callback(-ETIMEDOUT, dsi.framedone_data); dsi.framedone_callback(error, dsi.framedone_data);
if (!error)
dsi_perf_show("DISPC");
} }
static void dsi_framedone_irq_callback(void *data, u32 mask) static void dsi_framedone_timeout_work_callback(struct work_struct *work)
{ {
/* Note: We get FRAMEDONE when DISPC has finished sending pixels and /* XXX While extremely unlikely, we could get FRAMEDONE interrupt after
* turns itself off. However, DSI still has the pixels in its buffers, * 250ms which would conflict with this timeout work. What should be
* and is sending the data. * done is first cancel the transfer on the HW, and then cancel the
*/ * possibly scheduled framedone work. However, cancelling the transfer
* on the HW is buggy, and would probably require resetting the whole
* DSI */
/* SIDLEMODE back to smart-idle */ DSSERR("Framedone not received for 250ms!\n");
dispc_enable_sidle();
schedule_work(&dsi.framedone_work); dsi_handle_framedone(-ETIMEDOUT);
} }
static void dsi_handle_framedone(void) static void dsi_framedone_bta_callback(void)
{
dsi_handle_framedone(0);
#ifdef CONFIG_OMAP2_DSS_FAKE_VSYNC
dispc_fake_vsync_irq();
#endif
}
static void dsi_framedone_irq_callback(void *data, u32 mask)
{ {
int r;
const int channel = dsi.update_channel; const int channel = dsi.update_channel;
int r;
DSSDBG("FRAMEDONE\n"); /* Note: We get FRAMEDONE when DISPC has finished sending pixels and
* turns itself off. However, DSI still has the pixels in its buffers,
* and is sending the data.
*/
if (dsi.te_enabled) { if (dsi.te_enabled) {
/* enable LP_RX_TO again after the TE */ /* enable LP_RX_TO again after the TE */
...@@ -2904,37 +2839,30 @@ static void dsi_handle_framedone(void) ...@@ -2904,37 +2839,30 @@ static void dsi_handle_framedone(void)
* This is not needed when TE is not in use, but we do it anyway to * This is not needed when TE is not in use, but we do it anyway to
* make sure that the transfer has been completed. It would be more * make sure that the transfer has been completed. It would be more
* optimal, but more complex, to wait only just before starting next * optimal, but more complex, to wait only just before starting next
* transfer. */ * transfer.
r = dsi_vc_send_bta_sync(channel); *
if (r) * Also, as there's no interrupt telling when the transfer has been
DSSERR("BTA after framedone failed\n"); * done and the channel could be reconfigured, the only way is to
* busyloop until TE_SIZE is zero. With BTA we can do this
/* RX_FIFO_NOT_EMPTY */ * asynchronously.
if (REG_GET(DSI_VC_CTRL(channel), 20, 20)) { * */
DSSERR("Received error during frame transfer:\n");
dsi_vc_flush_receive_data(channel);
}
#ifdef CONFIG_OMAP2_DSS_FAKE_VSYNC
dispc_fake_vsync_irq();
#endif
}
static void dsi_framedone_work_callback(struct work_struct *work)
{
DSSDBGF();
cancel_delayed_work_sync(&dsi.framedone_timeout_work); dsi.bta_callback = dsi_framedone_bta_callback;
dsi_handle_framedone(); barrier();
dsi_perf_show("DISPC"); dsi_vc_enable_bta_irq(channel);
dsi.framedone_callback(0, dsi.framedone_data); r = dsi_vc_send_bta(channel);
if (r) {
DSSERR("BTA after framedone failed\n");
dsi_handle_framedone(-EIO);
}
} }
int omap_dsi_prepare_update(struct omap_dss_device *dssdev, int omap_dsi_prepare_update(struct omap_dss_device *dssdev,
u16 *x, u16 *y, u16 *w, u16 *h) u16 *x, u16 *y, u16 *w, u16 *h,
bool enlarge_update_area)
{ {
u16 dw, dh; u16 dw, dh;
...@@ -2958,7 +2886,8 @@ int omap_dsi_prepare_update(struct omap_dss_device *dssdev, ...@@ -2958,7 +2886,8 @@ int omap_dsi_prepare_update(struct omap_dss_device *dssdev,
dsi_perf_mark_setup(); dsi_perf_mark_setup();
if (dssdev->manager->caps & OMAP_DSS_OVL_MGR_CAP_DISPC) { if (dssdev->manager->caps & OMAP_DSS_OVL_MGR_CAP_DISPC) {
dss_setup_partial_planes(dssdev, x, y, w, h); dss_setup_partial_planes(dssdev, x, y, w, h,
enlarge_update_area);
dispc_set_lcd_size(*w, *h); dispc_set_lcd_size(*w, *h);
} }
...@@ -2973,6 +2902,12 @@ int omap_dsi_update(struct omap_dss_device *dssdev, ...@@ -2973,6 +2902,12 @@ int omap_dsi_update(struct omap_dss_device *dssdev,
{ {
dsi.update_channel = channel; dsi.update_channel = channel;
/* OMAP DSS cannot send updates of odd widths.
* omap_dsi_prepare_update() makes the widths even, but add a BUG_ON
* here to make sure we catch erroneous updates. Otherwise we'll only
* see rather obscure HW error happening, as DSS halts. */
BUG_ON(x % 2 == 1);
if (dssdev->manager->caps & OMAP_DSS_OVL_MGR_CAP_DISPC) { if (dssdev->manager->caps & OMAP_DSS_OVL_MGR_CAP_DISPC) {
dsi.framedone_callback = callback; dsi.framedone_callback = callback;
dsi.framedone_data = data; dsi.framedone_data = data;
...@@ -2985,7 +2920,12 @@ int omap_dsi_update(struct omap_dss_device *dssdev, ...@@ -2985,7 +2920,12 @@ int omap_dsi_update(struct omap_dss_device *dssdev,
dsi_update_screen_dispc(dssdev, x, y, w, h); dsi_update_screen_dispc(dssdev, x, y, w, h);
} else { } else {
dsi_update_screen_l4(dssdev, x, y, w, h); int r;
r = dsi_update_screen_l4(dssdev, x, y, w, h);
if (r)
return r;
dsi_perf_show("L4"); dsi_perf_show("L4");
callback(0, data); callback(0, data);
} }
...@@ -3048,8 +2988,10 @@ static int dsi_configure_dsi_clocks(struct omap_dss_device *dssdev) ...@@ -3048,8 +2988,10 @@ static int dsi_configure_dsi_clocks(struct omap_dss_device *dssdev)
cinfo.regm3 = dssdev->phy.dsi.div.regm3; cinfo.regm3 = dssdev->phy.dsi.div.regm3;
cinfo.regm4 = dssdev->phy.dsi.div.regm4; cinfo.regm4 = dssdev->phy.dsi.div.regm4;
r = dsi_calc_clock_rates(&cinfo); r = dsi_calc_clock_rates(&cinfo);
if (r) if (r) {
DSSERR("Failed to calc dsi clocks\n");
return r; return r;
}
r = dsi_pll_set_clock_div(&cinfo); r = dsi_pll_set_clock_div(&cinfo);
if (r) { if (r) {
...@@ -3147,6 +3089,13 @@ static int dsi_display_init_dsi(struct omap_dss_device *dssdev) ...@@ -3147,6 +3089,13 @@ static int dsi_display_init_dsi(struct omap_dss_device *dssdev)
static void dsi_display_uninit_dsi(struct omap_dss_device *dssdev) static void dsi_display_uninit_dsi(struct omap_dss_device *dssdev)
{ {
/* disable interface */
dsi_if_enable(0);
dsi_vc_enable(0, 0);
dsi_vc_enable(1, 0);
dsi_vc_enable(2, 0);
dsi_vc_enable(3, 0);
dss_select_dispc_clk_source(DSS_SRC_DSS1_ALWON_FCLK); dss_select_dispc_clk_source(DSS_SRC_DSS1_ALWON_FCLK);
dss_select_dsi_clk_source(DSS_SRC_DSS1_ALWON_FCLK); dss_select_dsi_clk_source(DSS_SRC_DSS1_ALWON_FCLK);
dsi_complexio_uninit(); dsi_complexio_uninit();
...@@ -3257,7 +3206,7 @@ void dsi_get_overlay_fifo_thresholds(enum omap_plane plane, ...@@ -3257,7 +3206,7 @@ void dsi_get_overlay_fifo_thresholds(enum omap_plane plane,
burst_size_bytes = 16 * 32 / 8; burst_size_bytes = 16 * 32 / 8;
*fifo_high = fifo_size - burst_size_bytes; *fifo_high = fifo_size - burst_size_bytes;
*fifo_low = fifo_size - burst_size_bytes * 8; *fifo_low = fifo_size - burst_size_bytes * 2;
} }
int dsi_init_display(struct omap_dss_device *dssdev) int dsi_init_display(struct omap_dss_device *dssdev)
...@@ -3274,6 +3223,18 @@ int dsi_init_display(struct omap_dss_device *dssdev) ...@@ -3274,6 +3223,18 @@ int dsi_init_display(struct omap_dss_device *dssdev)
return 0; return 0;
} }
void dsi_wait_dsi1_pll_active(void)
{
if (wait_for_bit_change(DSI_PLL_STATUS, 7, 1) != 1)
DSSERR("DSI1 PLL clock not active\n");
}
void dsi_wait_dsi2_pll_active(void)
{
if (wait_for_bit_change(DSI_PLL_STATUS, 8, 1) != 1)
DSSERR("DSI2 PLL clock not active\n");
}
int dsi_init(struct platform_device *pdev) int dsi_init(struct platform_device *pdev)
{ {
u32 rev; u32 rev;
...@@ -3292,7 +3253,10 @@ int dsi_init(struct platform_device *pdev) ...@@ -3292,7 +3253,10 @@ int dsi_init(struct platform_device *pdev)
mutex_init(&dsi.lock); mutex_init(&dsi.lock);
sema_init(&dsi.bus_lock, 1); sema_init(&dsi.bus_lock, 1);
INIT_WORK(&dsi.framedone_work, dsi_framedone_work_callback); dsi.workqueue = create_singlethread_workqueue("dsi");
if (dsi.workqueue == NULL)
return -ENOMEM;
INIT_DELAYED_WORK_DEFERRABLE(&dsi.framedone_timeout_work, INIT_DELAYED_WORK_DEFERRABLE(&dsi.framedone_timeout_work,
dsi_framedone_timeout_work_callback); dsi_framedone_timeout_work_callback);
...@@ -3328,6 +3292,7 @@ int dsi_init(struct platform_device *pdev) ...@@ -3328,6 +3292,7 @@ int dsi_init(struct platform_device *pdev)
err2: err2:
iounmap(dsi.base); iounmap(dsi.base);
err1: err1:
destroy_workqueue(dsi.workqueue);
return r; return r;
} }
...@@ -3335,6 +3300,8 @@ void dsi_exit(void) ...@@ -3335,6 +3300,8 @@ void dsi_exit(void)
{ {
iounmap(dsi.base); iounmap(dsi.base);
destroy_workqueue(dsi.workqueue);
DSSDBG("omap_dsi_exit\n"); DSSDBG("omap_dsi_exit\n");
} }
...@@ -265,6 +265,9 @@ void dss_select_dispc_clk_source(enum dss_clk_source clk_src) ...@@ -265,6 +265,9 @@ void dss_select_dispc_clk_source(enum dss_clk_source clk_src)
b = clk_src == DSS_SRC_DSS1_ALWON_FCLK ? 0 : 1; b = clk_src == DSS_SRC_DSS1_ALWON_FCLK ? 0 : 1;
if (clk_src == DSS_SRC_DSI1_PLL_FCLK)
dsi_wait_dsi1_pll_active();
REG_FLD_MOD(DSS_CONTROL, b, 0, 0); /* DISPC_CLK_SWITCH */ REG_FLD_MOD(DSS_CONTROL, b, 0, 0); /* DISPC_CLK_SWITCH */
dss.dispc_clk_source = clk_src; dss.dispc_clk_source = clk_src;
...@@ -279,6 +282,9 @@ void dss_select_dsi_clk_source(enum dss_clk_source clk_src) ...@@ -279,6 +282,9 @@ void dss_select_dsi_clk_source(enum dss_clk_source clk_src)
b = clk_src == DSS_SRC_DSS1_ALWON_FCLK ? 0 : 1; b = clk_src == DSS_SRC_DSS1_ALWON_FCLK ? 0 : 1;
if (clk_src == DSS_SRC_DSI2_PLL_FCLK)
dsi_wait_dsi2_pll_active();
REG_FLD_MOD(DSS_CONTROL, b, 1, 1); /* DSI_CLK_SWITCH */ REG_FLD_MOD(DSS_CONTROL, b, 1, 1); /* DSI_CLK_SWITCH */
dss.dsi_clk_source = clk_src; dss.dsi_clk_source = clk_src;
......
...@@ -199,7 +199,8 @@ int dss_init_overlay_managers(struct platform_device *pdev); ...@@ -199,7 +199,8 @@ int dss_init_overlay_managers(struct platform_device *pdev);
void dss_uninit_overlay_managers(struct platform_device *pdev); void dss_uninit_overlay_managers(struct platform_device *pdev);
int dss_mgr_wait_for_go_ovl(struct omap_overlay *ovl); int dss_mgr_wait_for_go_ovl(struct omap_overlay *ovl);
void dss_setup_partial_planes(struct omap_dss_device *dssdev, void dss_setup_partial_planes(struct omap_dss_device *dssdev,
u16 *x, u16 *y, u16 *w, u16 *h); u16 *x, u16 *y, u16 *w, u16 *h,
bool enlarge_update_area);
void dss_start_update(struct omap_dss_device *dssdev); void dss_start_update(struct omap_dss_device *dssdev);
/* overlay */ /* overlay */
...@@ -281,6 +282,8 @@ void dsi_pll_uninit(void); ...@@ -281,6 +282,8 @@ void dsi_pll_uninit(void);
void dsi_get_overlay_fifo_thresholds(enum omap_plane plane, void dsi_get_overlay_fifo_thresholds(enum omap_plane plane,
u32 fifo_size, enum omap_burst_size *burst_size, u32 fifo_size, enum omap_burst_size *burst_size,
u32 *fifo_low, u32 *fifo_high); u32 *fifo_low, u32 *fifo_high);
void dsi_wait_dsi1_pll_active(void);
void dsi_wait_dsi2_pll_active(void);
#else #else
static inline int dsi_init(struct platform_device *pdev) static inline int dsi_init(struct platform_device *pdev)
{ {
...@@ -289,6 +292,12 @@ static inline int dsi_init(struct platform_device *pdev) ...@@ -289,6 +292,12 @@ static inline int dsi_init(struct platform_device *pdev)
static inline void dsi_exit(void) static inline void dsi_exit(void)
{ {
} }
static inline void dsi_wait_dsi1_pll_active(void)
{
}
static inline void dsi_wait_dsi2_pll_active(void)
{
}
#endif #endif
/* DPI */ /* DPI */
......
...@@ -440,6 +440,10 @@ struct manager_cache_data { ...@@ -440,6 +440,10 @@ struct manager_cache_data {
/* manual update region */ /* manual update region */
u16 x, y, w, h; u16 x, y, w, h;
/* enlarge the update area if the update area contains scaled
* overlays */
bool enlarge_update_area;
}; };
static struct { static struct {
...@@ -525,7 +529,7 @@ static int dss_mgr_wait_for_go(struct omap_overlay_manager *mgr) ...@@ -525,7 +529,7 @@ static int dss_mgr_wait_for_go(struct omap_overlay_manager *mgr)
int i; int i;
struct omap_dss_device *dssdev = mgr->device; struct omap_dss_device *dssdev = mgr->device;
if (!dssdev) if (!dssdev || dssdev->state != OMAP_DSS_DISPLAY_ACTIVE)
return 0; return 0;
if (dssdev->type == OMAP_DISPLAY_TYPE_VENC) { if (dssdev->type == OMAP_DISPLAY_TYPE_VENC) {
...@@ -596,11 +600,14 @@ int dss_mgr_wait_for_go_ovl(struct omap_overlay *ovl) ...@@ -596,11 +600,14 @@ int dss_mgr_wait_for_go_ovl(struct omap_overlay *ovl)
int r; int r;
int i; int i;
if (!ovl->manager || !ovl->manager->device) if (!ovl->manager)
return 0; return 0;
dssdev = ovl->manager->device; dssdev = ovl->manager->device;
if (!dssdev || dssdev->state != OMAP_DSS_DISPLAY_ACTIVE)
return 0;
if (dssdev->type == OMAP_DISPLAY_TYPE_VENC) { if (dssdev->type == OMAP_DISPLAY_TYPE_VENC) {
irq = DISPC_IRQ_EVSYNC_ODD | DISPC_IRQ_EVSYNC_EVEN; irq = DISPC_IRQ_EVSYNC_ODD | DISPC_IRQ_EVSYNC_EVEN;
channel = OMAP_DSS_CHANNEL_DIGIT; channel = OMAP_DSS_CHANNEL_DIGIT;
...@@ -718,6 +725,7 @@ static int configure_overlay(enum omap_plane plane) ...@@ -718,6 +725,7 @@ static int configure_overlay(enum omap_plane plane)
u16 x, y, w, h; u16 x, y, w, h;
u32 paddr; u32 paddr;
int r; int r;
u16 orig_w, orig_h, orig_outw, orig_outh;
DSSDBGF("%d", plane); DSSDBGF("%d", plane);
...@@ -738,8 +746,16 @@ static int configure_overlay(enum omap_plane plane) ...@@ -738,8 +746,16 @@ static int configure_overlay(enum omap_plane plane)
outh = c->out_height == 0 ? c->height : c->out_height; outh = c->out_height == 0 ? c->height : c->out_height;
paddr = c->paddr; paddr = c->paddr;
orig_w = w;
orig_h = h;
orig_outw = outw;
orig_outh = outh;
if (c->manual_update && mc->do_manual_update) { if (c->manual_update && mc->do_manual_update) {
unsigned bpp; unsigned bpp;
unsigned scale_x_m = w, scale_x_d = outw;
unsigned scale_y_m = h, scale_y_d = outh;
/* If the overlay is outside the update region, disable it */ /* If the overlay is outside the update region, disable it */
if (!rectangle_intersects(mc->x, mc->y, mc->w, mc->h, if (!rectangle_intersects(mc->x, mc->y, mc->w, mc->h,
x, y, outw, outh)) { x, y, outw, outh)) {
...@@ -770,38 +786,47 @@ static int configure_overlay(enum omap_plane plane) ...@@ -770,38 +786,47 @@ static int configure_overlay(enum omap_plane plane)
BUG(); BUG();
} }
if (dispc_is_overlay_scaled(c)) { if (mc->x > c->pos_x) {
/* If the overlay is scaled, the update area has x = 0;
* already been enlarged to cover the whole overlay. We outw -= (mc->x - c->pos_x);
* only need to adjust x/y here */ paddr += (mc->x - c->pos_x) *
x = c->pos_x - mc->x; scale_x_m / scale_x_d * bpp / 8;
y = c->pos_y - mc->y;
} else { } else {
if (mc->x > c->pos_x) { x = c->pos_x - mc->x;
x = 0; }
w -= (mc->x - c->pos_x);
paddr += (mc->x - c->pos_x) * bpp / 8;
} else {
x = c->pos_x - mc->x;
}
if (mc->y > c->pos_y) {
y = 0;
h -= (mc->y - c->pos_y);
paddr += (mc->y - c->pos_y) * c->screen_width *
bpp / 8;
} else {
y = c->pos_y - mc->y;
}
if (mc->w < (x+w))
w -= (x+w) - (mc->w);
if (mc->h < (y+h)) if (mc->y > c->pos_y) {
h -= (y+h) - (mc->h); y = 0;
outh -= (mc->y - c->pos_y);
paddr += (mc->y - c->pos_y) *
scale_y_m / scale_y_d *
c->screen_width * bpp / 8;
} else {
y = c->pos_y - mc->y;
}
outw = w; if (mc->w < (x + outw))
outh = h; outw -= (x + outw) - (mc->w);
if (mc->h < (y + outh))
outh -= (y + outh) - (mc->h);
w = w * outw / orig_outw;
h = h * outh / orig_outh;
/* YUV mode overlay's input width has to be even and the
* algorithm above may adjust the width to be odd.
*
* Here we adjust the width if needed, preferring to increase
* the width if the original width was bigger.
*/
if ((w & 1) &&
(c->color_mode == OMAP_DSS_COLOR_YUV2 ||
c->color_mode == OMAP_DSS_COLOR_UYVY)) {
if (orig_w > w)
w += 1;
else
w -= 1;
} }
} }
...@@ -960,7 +985,7 @@ static void make_even(u16 *x, u16 *w) ...@@ -960,7 +985,7 @@ static void make_even(u16 *x, u16 *w)
/* Configure dispc for partial update. Return possibly modified update /* Configure dispc for partial update. Return possibly modified update
* area */ * area */
void dss_setup_partial_planes(struct omap_dss_device *dssdev, void dss_setup_partial_planes(struct omap_dss_device *dssdev,
u16 *xi, u16 *yi, u16 *wi, u16 *hi) u16 *xi, u16 *yi, u16 *wi, u16 *hi, bool enlarge_update_area)
{ {
struct overlay_cache_data *oc; struct overlay_cache_data *oc;
struct manager_cache_data *mc; struct manager_cache_data *mc;
...@@ -969,6 +994,7 @@ void dss_setup_partial_planes(struct omap_dss_device *dssdev, ...@@ -969,6 +994,7 @@ void dss_setup_partial_planes(struct omap_dss_device *dssdev,
int i; int i;
u16 x, y, w, h; u16 x, y, w, h;
unsigned long flags; unsigned long flags;
bool area_changed;
x = *xi; x = *xi;
y = *yi; y = *yi;
...@@ -989,73 +1015,91 @@ void dss_setup_partial_planes(struct omap_dss_device *dssdev, ...@@ -989,73 +1015,91 @@ void dss_setup_partial_planes(struct omap_dss_device *dssdev,
spin_lock_irqsave(&dss_cache.lock, flags); spin_lock_irqsave(&dss_cache.lock, flags);
/* We need to show the whole overlay if it is scaled. So look for /*
* those, and make the update area larger if found. * Execute the outer loop until the inner loop has completed
* Also mark the overlay cache dirty */ * once without increasing the update area. This will ensure that
for (i = 0; i < num_ovls; ++i) { * all scaled overlays end up completely within the update area.
unsigned x1, y1, x2, y2; */
unsigned outw, outh; do {
area_changed = false;
oc = &dss_cache.overlay_cache[i]; /* We need to show the whole overlay if it is scaled. So look
* for those, and make the update area larger if found.
* Also mark the overlay cache dirty */
for (i = 0; i < num_ovls; ++i) {
unsigned x1, y1, x2, y2;
unsigned outw, outh;
if (oc->channel != mgr->id) oc = &dss_cache.overlay_cache[i];
continue;
oc->dirty = true; if (oc->channel != mgr->id)
continue;
if (!oc->enabled) oc->dirty = true;
continue;
if (!dispc_is_overlay_scaled(oc)) if (!enlarge_update_area)
continue; continue;
outw = oc->out_width == 0 ? oc->width : oc->out_width; if (!oc->enabled)
outh = oc->out_height == 0 ? oc->height : oc->out_height; continue;
/* is the overlay outside the update region? */ if (!dispc_is_overlay_scaled(oc))
if (!rectangle_intersects(x, y, w, h, continue;
oc->pos_x, oc->pos_y,
outw, outh))
continue;
/* if the overlay totally inside the update region? */ outw = oc->out_width == 0 ?
if (rectangle_subset(oc->pos_x, oc->pos_y, outw, outh, oc->width : oc->out_width;
x, y, w, h)) outh = oc->out_height == 0 ?
continue; oc->height : oc->out_height;
/* is the overlay outside the update region? */
if (!rectangle_intersects(x, y, w, h,
oc->pos_x, oc->pos_y,
outw, outh))
continue;
if (x > oc->pos_x) /* if the overlay totally inside the update region? */
x1 = oc->pos_x; if (rectangle_subset(oc->pos_x, oc->pos_y, outw, outh,
else x, y, w, h))
x1 = x; continue;
if (y > oc->pos_y) if (x > oc->pos_x)
y1 = oc->pos_y; x1 = oc->pos_x;
else else
y1 = y; x1 = x;
if ((x + w) < (oc->pos_x + outw)) if (y > oc->pos_y)
x2 = oc->pos_x + outw; y1 = oc->pos_y;
else else
x2 = x + w; y1 = y;
if ((y + h) < (oc->pos_y + outh)) if ((x + w) < (oc->pos_x + outw))
y2 = oc->pos_y + outh; x2 = oc->pos_x + outw;
else else
y2 = y + h; x2 = x + w;
x = x1; if ((y + h) < (oc->pos_y + outh))
y = y1; y2 = oc->pos_y + outh;
w = x2 - x1; else
h = y2 - y1; y2 = y + h;
make_even(&x, &w); x = x1;
y = y1;
w = x2 - x1;
h = y2 - y1;
DSSDBG("changing upd area due to ovl(%d) scaling %d,%d %dx%d\n", make_even(&x, &w);
DSSDBG("changing upd area due to ovl(%d) "
"scaling %d,%d %dx%d\n",
i, x, y, w, h); i, x, y, w, h);
}
area_changed = true;
}
} while (area_changed);
mc = &dss_cache.manager_cache[mgr->id]; mc = &dss_cache.manager_cache[mgr->id];
mc->do_manual_update = true; mc->do_manual_update = true;
mc->enlarge_update_area = enlarge_update_area;
mc->x = x; mc->x = x;
mc->y = y; mc->y = y;
mc->w = w; mc->w = w;
......
...@@ -65,7 +65,7 @@ static ssize_t overlay_manager_store(struct omap_overlay *ovl, const char *buf, ...@@ -65,7 +65,7 @@ static ssize_t overlay_manager_store(struct omap_overlay *ovl, const char *buf,
for (i = 0; i < omap_dss_get_num_overlay_managers(); ++i) { for (i = 0; i < omap_dss_get_num_overlay_managers(); ++i) {
mgr = omap_dss_get_overlay_manager(i); mgr = omap_dss_get_overlay_manager(i);
if (strncmp(buf, mgr->name, len) == 0) if (sysfs_streq(buf, mgr->name))
break; break;
mgr = NULL; mgr = NULL;
......
...@@ -886,7 +886,7 @@ int omap_rfbi_prepare_update(struct omap_dss_device *dssdev, ...@@ -886,7 +886,7 @@ int omap_rfbi_prepare_update(struct omap_dss_device *dssdev,
return -EINVAL; return -EINVAL;
if (dssdev->manager->caps & OMAP_DSS_OVL_MGR_CAP_DISPC) { if (dssdev->manager->caps & OMAP_DSS_OVL_MGR_CAP_DISPC) {
dss_setup_partial_planes(dssdev, x, y, w, h); dss_setup_partial_planes(dssdev, x, y, w, h, true);
dispc_set_lcd_size(*w, *h); dispc_set_lcd_size(*w, *h);
} }
......
...@@ -34,12 +34,37 @@ ...@@ -34,12 +34,37 @@
#include "omapfb.h" #include "omapfb.h"
static u8 get_mem_idx(struct omapfb_info *ofbi)
{
if (ofbi->id == ofbi->region->id)
return 0;
return OMAPFB_MEM_IDX_ENABLED | ofbi->region->id;
}
static struct omapfb2_mem_region *get_mem_region(struct omapfb_info *ofbi,
u8 mem_idx)
{
struct omapfb2_device *fbdev = ofbi->fbdev;
if (mem_idx & OMAPFB_MEM_IDX_ENABLED)
mem_idx &= OMAPFB_MEM_IDX_MASK;
else
mem_idx = ofbi->id;
if (mem_idx >= fbdev->num_fbs)
return NULL;
return &fbdev->regions[mem_idx];
}
static int omapfb_setup_plane(struct fb_info *fbi, struct omapfb_plane_info *pi) static int omapfb_setup_plane(struct fb_info *fbi, struct omapfb_plane_info *pi)
{ {
struct omapfb_info *ofbi = FB2OFB(fbi); struct omapfb_info *ofbi = FB2OFB(fbi);
struct omapfb2_device *fbdev = ofbi->fbdev; struct omapfb2_device *fbdev = ofbi->fbdev;
struct omap_overlay *ovl; struct omap_overlay *ovl;
struct omap_overlay_info info; struct omap_overlay_info old_info;
struct omapfb2_mem_region *old_rg, *new_rg;
int r = 0; int r = 0;
DBG("omapfb_setup_plane\n"); DBG("omapfb_setup_plane\n");
...@@ -52,36 +77,106 @@ static int omapfb_setup_plane(struct fb_info *fbi, struct omapfb_plane_info *pi) ...@@ -52,36 +77,106 @@ static int omapfb_setup_plane(struct fb_info *fbi, struct omapfb_plane_info *pi)
/* XXX uses only the first overlay */ /* XXX uses only the first overlay */
ovl = ofbi->overlays[0]; ovl = ofbi->overlays[0];
if (pi->enabled && !ofbi->region.size) { old_rg = ofbi->region;
new_rg = get_mem_region(ofbi, pi->mem_idx);
if (!new_rg) {
r = -EINVAL;
goto out;
}
/* Take the locks in a specific order to keep lockdep happy */
if (old_rg->id < new_rg->id) {
omapfb_get_mem_region(old_rg);
omapfb_get_mem_region(new_rg);
} else if (new_rg->id < old_rg->id) {
omapfb_get_mem_region(new_rg);
omapfb_get_mem_region(old_rg);
} else
omapfb_get_mem_region(old_rg);
if (pi->enabled && !new_rg->size) {
/* /*
* This plane's memory was freed, can't enable it * This plane's memory was freed, can't enable it
* until it's reallocated. * until it's reallocated.
*/ */
r = -EINVAL; r = -EINVAL;
goto out; goto put_mem;
} }
ovl->get_overlay_info(ovl, &info); ovl->get_overlay_info(ovl, &old_info);
info.pos_x = pi->pos_x; if (old_rg != new_rg) {
info.pos_y = pi->pos_y; ofbi->region = new_rg;
info.out_width = pi->out_width; set_fb_fix(fbi);
info.out_height = pi->out_height; }
info.enabled = pi->enabled;
r = ovl->set_overlay_info(ovl, &info); if (pi->enabled) {
if (r) struct omap_overlay_info info;
goto out;
if (ovl->manager) { r = omapfb_setup_overlay(fbi, ovl, pi->pos_x, pi->pos_y,
r = ovl->manager->apply(ovl->manager); pi->out_width, pi->out_height);
if (r) if (r)
goto out; goto undo;
ovl->get_overlay_info(ovl, &info);
if (!info.enabled) {
info.enabled = pi->enabled;
r = ovl->set_overlay_info(ovl, &info);
if (r)
goto undo;
}
} else {
struct omap_overlay_info info;
ovl->get_overlay_info(ovl, &info);
info.enabled = pi->enabled;
info.pos_x = pi->pos_x;
info.pos_y = pi->pos_y;
info.out_width = pi->out_width;
info.out_height = pi->out_height;
r = ovl->set_overlay_info(ovl, &info);
if (r)
goto undo;
} }
out: if (ovl->manager)
if (r) ovl->manager->apply(ovl->manager);
dev_err(fbdev->dev, "setup_plane failed\n");
/* Release the locks in a specific order to keep lockdep happy */
if (old_rg->id > new_rg->id) {
omapfb_put_mem_region(old_rg);
omapfb_put_mem_region(new_rg);
} else if (new_rg->id > old_rg->id) {
omapfb_put_mem_region(new_rg);
omapfb_put_mem_region(old_rg);
} else
omapfb_put_mem_region(old_rg);
return 0;
undo:
if (old_rg != new_rg) {
ofbi->region = old_rg;
set_fb_fix(fbi);
}
ovl->set_overlay_info(ovl, &old_info);
put_mem:
/* Release the locks in a specific order to keep lockdep happy */
if (old_rg->id > new_rg->id) {
omapfb_put_mem_region(old_rg);
omapfb_put_mem_region(new_rg);
} else if (new_rg->id > old_rg->id) {
omapfb_put_mem_region(new_rg);
omapfb_put_mem_region(old_rg);
} else
omapfb_put_mem_region(old_rg);
out:
dev_err(fbdev->dev, "setup_plane failed\n");
return r; return r;
} }
...@@ -92,8 +187,8 @@ static int omapfb_query_plane(struct fb_info *fbi, struct omapfb_plane_info *pi) ...@@ -92,8 +187,8 @@ static int omapfb_query_plane(struct fb_info *fbi, struct omapfb_plane_info *pi)
if (ofbi->num_overlays != 1) { if (ofbi->num_overlays != 1) {
memset(pi, 0, sizeof(*pi)); memset(pi, 0, sizeof(*pi));
} else { } else {
struct omap_overlay_info *ovli;
struct omap_overlay *ovl; struct omap_overlay *ovl;
struct omap_overlay_info *ovli;
ovl = ofbi->overlays[0]; ovl = ofbi->overlays[0];
ovli = &ovl->info; ovli = &ovl->info;
...@@ -103,6 +198,7 @@ static int omapfb_query_plane(struct fb_info *fbi, struct omapfb_plane_info *pi) ...@@ -103,6 +198,7 @@ static int omapfb_query_plane(struct fb_info *fbi, struct omapfb_plane_info *pi)
pi->enabled = ovli->enabled; pi->enabled = ovli->enabled;
pi->channel_out = 0; /* xxx */ pi->channel_out = 0; /* xxx */
pi->mirror = 0; pi->mirror = 0;
pi->mem_idx = get_mem_idx(ofbi);
pi->out_width = ovli->out_width; pi->out_width = ovli->out_width;
pi->out_height = ovli->out_height; pi->out_height = ovli->out_height;
} }
...@@ -115,7 +211,7 @@ static int omapfb_setup_mem(struct fb_info *fbi, struct omapfb_mem_info *mi) ...@@ -115,7 +211,7 @@ static int omapfb_setup_mem(struct fb_info *fbi, struct omapfb_mem_info *mi)
struct omapfb_info *ofbi = FB2OFB(fbi); struct omapfb_info *ofbi = FB2OFB(fbi);
struct omapfb2_device *fbdev = ofbi->fbdev; struct omapfb2_device *fbdev = ofbi->fbdev;
struct omapfb2_mem_region *rg; struct omapfb2_mem_region *rg;
int r, i; int r = 0, i;
size_t size; size_t size;
if (mi->type > OMAPFB_MEMTYPE_MAX) if (mi->type > OMAPFB_MEMTYPE_MAX)
...@@ -123,22 +219,44 @@ static int omapfb_setup_mem(struct fb_info *fbi, struct omapfb_mem_info *mi) ...@@ -123,22 +219,44 @@ static int omapfb_setup_mem(struct fb_info *fbi, struct omapfb_mem_info *mi)
size = PAGE_ALIGN(mi->size); size = PAGE_ALIGN(mi->size);
rg = &ofbi->region; rg = ofbi->region;
for (i = 0; i < ofbi->num_overlays; i++) { down_write_nested(&rg->lock, rg->id);
if (ofbi->overlays[i]->info.enabled) atomic_inc(&rg->lock_count);
return -EBUSY;
if (atomic_read(&rg->map_count)) {
r = -EBUSY;
goto out;
}
for (i = 0; i < fbdev->num_fbs; i++) {
struct omapfb_info *ofbi2 = FB2OFB(fbdev->fbs[i]);
int j;
if (ofbi2->region != rg)
continue;
for (j = 0; j < ofbi2->num_overlays; j++) {
if (ofbi2->overlays[j]->info.enabled) {
r = -EBUSY;
goto out;
}
}
} }
if (rg->size != size || rg->type != mi->type) { if (rg->size != size || rg->type != mi->type) {
r = omapfb_realloc_fbmem(fbi, size, mi->type); r = omapfb_realloc_fbmem(fbi, size, mi->type);
if (r) { if (r) {
dev_err(fbdev->dev, "realloc fbmem failed\n"); dev_err(fbdev->dev, "realloc fbmem failed\n");
return r; goto out;
} }
} }
return 0; out:
atomic_dec(&rg->lock_count);
up_write(&rg->lock);
return r;
} }
static int omapfb_query_mem(struct fb_info *fbi, struct omapfb_mem_info *mi) static int omapfb_query_mem(struct fb_info *fbi, struct omapfb_mem_info *mi)
...@@ -146,12 +264,14 @@ static int omapfb_query_mem(struct fb_info *fbi, struct omapfb_mem_info *mi) ...@@ -146,12 +264,14 @@ static int omapfb_query_mem(struct fb_info *fbi, struct omapfb_mem_info *mi)
struct omapfb_info *ofbi = FB2OFB(fbi); struct omapfb_info *ofbi = FB2OFB(fbi);
struct omapfb2_mem_region *rg; struct omapfb2_mem_region *rg;
rg = &ofbi->region; rg = omapfb_get_mem_region(ofbi->region);
memset(mi, 0, sizeof(*mi)); memset(mi, 0, sizeof(*mi));
mi->size = rg->size; mi->size = rg->size;
mi->type = rg->type; mi->type = rg->type;
omapfb_put_mem_region(rg);
return 0; return 0;
} }
...@@ -490,6 +610,7 @@ int omapfb_ioctl(struct fb_info *fbi, unsigned int cmd, unsigned long arg) ...@@ -490,6 +610,7 @@ int omapfb_ioctl(struct fb_info *fbi, unsigned int cmd, unsigned long arg)
struct omapfb_vram_info vram_info; struct omapfb_vram_info vram_info;
struct omapfb_tearsync_info tearsync_info; struct omapfb_tearsync_info tearsync_info;
struct omapfb_display_info display_info; struct omapfb_display_info display_info;
u32 crt;
} p; } p;
int r = 0; int r = 0;
...@@ -648,6 +769,17 @@ int omapfb_ioctl(struct fb_info *fbi, unsigned int cmd, unsigned long arg) ...@@ -648,6 +769,17 @@ int omapfb_ioctl(struct fb_info *fbi, unsigned int cmd, unsigned long arg)
r = -EFAULT; r = -EFAULT;
break; break;
case FBIO_WAITFORVSYNC:
if (get_user(p.crt, (__u32 __user *)arg)) {
r = -EFAULT;
break;
}
if (p.crt != 0) {
r = -ENODEV;
break;
}
/* FALLTHROUGH */
case OMAPFB_WAITFORVSYNC: case OMAPFB_WAITFORVSYNC:
DBG("ioctl WAITFORVSYNC\n"); DBG("ioctl WAITFORVSYNC\n");
if (!display) { if (!display) {
...@@ -738,7 +870,7 @@ int omapfb_ioctl(struct fb_info *fbi, unsigned int cmd, unsigned long arg) ...@@ -738,7 +870,7 @@ int omapfb_ioctl(struct fb_info *fbi, unsigned int cmd, unsigned long arg)
break; break;
} }
if (!display->driver->enable_te) { if (!display || !display->driver->enable_te) {
r = -ENODEV; r = -ENODEV;
break; break;
} }
......
...@@ -157,7 +157,7 @@ static void fill_fb(struct fb_info *fbi) ...@@ -157,7 +157,7 @@ static void fill_fb(struct fb_info *fbi)
static unsigned omapfb_get_vrfb_offset(const struct omapfb_info *ofbi, int rot) static unsigned omapfb_get_vrfb_offset(const struct omapfb_info *ofbi, int rot)
{ {
const struct vrfb *vrfb = &ofbi->region.vrfb; const struct vrfb *vrfb = &ofbi->region->vrfb;
unsigned offset; unsigned offset;
switch (rot) { switch (rot) {
...@@ -185,27 +185,27 @@ static unsigned omapfb_get_vrfb_offset(const struct omapfb_info *ofbi, int rot) ...@@ -185,27 +185,27 @@ static unsigned omapfb_get_vrfb_offset(const struct omapfb_info *ofbi, int rot)
static u32 omapfb_get_region_rot_paddr(const struct omapfb_info *ofbi, int rot) static u32 omapfb_get_region_rot_paddr(const struct omapfb_info *ofbi, int rot)
{ {
if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB) { if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB) {
return ofbi->region.vrfb.paddr[rot] return ofbi->region->vrfb.paddr[rot]
+ omapfb_get_vrfb_offset(ofbi, rot); + omapfb_get_vrfb_offset(ofbi, rot);
} else { } else {
return ofbi->region.paddr; return ofbi->region->paddr;
} }
} }
static u32 omapfb_get_region_paddr(const struct omapfb_info *ofbi) static u32 omapfb_get_region_paddr(const struct omapfb_info *ofbi)
{ {
if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB) if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB)
return ofbi->region.vrfb.paddr[0]; return ofbi->region->vrfb.paddr[0];
else else
return ofbi->region.paddr; return ofbi->region->paddr;
} }
static void __iomem *omapfb_get_region_vaddr(const struct omapfb_info *ofbi) static void __iomem *omapfb_get_region_vaddr(const struct omapfb_info *ofbi)
{ {
if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB) if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB)
return ofbi->region.vrfb.vaddr[0]; return ofbi->region->vrfb.vaddr[0];
else else
return ofbi->region.vaddr; return ofbi->region->vaddr;
} }
static struct omapfb_colormode omapfb_colormodes[] = { static struct omapfb_colormode omapfb_colormodes[] = {
...@@ -450,7 +450,7 @@ static int check_vrfb_fb_size(unsigned long region_size, ...@@ -450,7 +450,7 @@ static int check_vrfb_fb_size(unsigned long region_size,
static int check_fb_size(const struct omapfb_info *ofbi, static int check_fb_size(const struct omapfb_info *ofbi,
struct fb_var_screeninfo *var) struct fb_var_screeninfo *var)
{ {
unsigned long max_frame_size = ofbi->region.size; unsigned long max_frame_size = ofbi->region->size;
int bytespp = var->bits_per_pixel >> 3; int bytespp = var->bits_per_pixel >> 3;
unsigned long line_size = var->xres_virtual * bytespp; unsigned long line_size = var->xres_virtual * bytespp;
...@@ -497,7 +497,7 @@ static int check_fb_size(const struct omapfb_info *ofbi, ...@@ -497,7 +497,7 @@ static int check_fb_size(const struct omapfb_info *ofbi,
static int setup_vrfb_rotation(struct fb_info *fbi) static int setup_vrfb_rotation(struct fb_info *fbi)
{ {
struct omapfb_info *ofbi = FB2OFB(fbi); struct omapfb_info *ofbi = FB2OFB(fbi);
struct omapfb2_mem_region *rg = &ofbi->region; struct omapfb2_mem_region *rg = ofbi->region;
struct vrfb *vrfb = &rg->vrfb; struct vrfb *vrfb = &rg->vrfb;
struct fb_var_screeninfo *var = &fbi->var; struct fb_var_screeninfo *var = &fbi->var;
struct fb_fix_screeninfo *fix = &fbi->fix; struct fb_fix_screeninfo *fix = &fbi->fix;
...@@ -558,9 +558,9 @@ static int setup_vrfb_rotation(struct fb_info *fbi) ...@@ -558,9 +558,9 @@ static int setup_vrfb_rotation(struct fb_info *fbi)
return r; return r;
/* used by open/write in fbmem.c */ /* used by open/write in fbmem.c */
fbi->screen_base = ofbi->region.vrfb.vaddr[0]; fbi->screen_base = ofbi->region->vrfb.vaddr[0];
fix->smem_start = ofbi->region.vrfb.paddr[0]; fix->smem_start = ofbi->region->vrfb.paddr[0];
switch (var->nonstd) { switch (var->nonstd) {
case OMAPFB_COLOR_YUV422: case OMAPFB_COLOR_YUV422:
...@@ -599,7 +599,7 @@ void set_fb_fix(struct fb_info *fbi) ...@@ -599,7 +599,7 @@ void set_fb_fix(struct fb_info *fbi)
struct fb_fix_screeninfo *fix = &fbi->fix; struct fb_fix_screeninfo *fix = &fbi->fix;
struct fb_var_screeninfo *var = &fbi->var; struct fb_var_screeninfo *var = &fbi->var;
struct omapfb_info *ofbi = FB2OFB(fbi); struct omapfb_info *ofbi = FB2OFB(fbi);
struct omapfb2_mem_region *rg = &ofbi->region; struct omapfb2_mem_region *rg = ofbi->region;
DBG("set_fb_fix\n"); DBG("set_fb_fix\n");
...@@ -668,8 +668,7 @@ int check_fb_var(struct fb_info *fbi, struct fb_var_screeninfo *var) ...@@ -668,8 +668,7 @@ int check_fb_var(struct fb_info *fbi, struct fb_var_screeninfo *var)
DBG("check_fb_var %d\n", ofbi->id); DBG("check_fb_var %d\n", ofbi->id);
if (ofbi->region.size == 0) WARN_ON(!atomic_read(&ofbi->region->lock_count));
return 0;
r = fb_mode_to_dss_mode(var, &mode); r = fb_mode_to_dss_mode(var, &mode);
if (r) { if (r) {
...@@ -684,13 +683,14 @@ int check_fb_var(struct fb_info *fbi, struct fb_var_screeninfo *var) ...@@ -684,13 +683,14 @@ int check_fb_var(struct fb_info *fbi, struct fb_var_screeninfo *var)
} }
} }
if (var->rotate < 0 || var->rotate > 3) if (var->rotate > 3)
return -EINVAL; return -EINVAL;
if (check_fb_res_bounds(var)) if (check_fb_res_bounds(var))
return -EINVAL; return -EINVAL;
if (check_fb_size(ofbi, var)) /* When no memory is allocated ignore the size check */
if (ofbi->region->size != 0 && check_fb_size(ofbi, var))
return -EINVAL; return -EINVAL;
if (var->xres + var->xoffset > var->xres_virtual) if (var->xres + var->xoffset > var->xres_virtual)
...@@ -822,9 +822,43 @@ static unsigned calc_rotation_offset_vrfb(const struct fb_var_screeninfo *var, ...@@ -822,9 +822,43 @@ static unsigned calc_rotation_offset_vrfb(const struct fb_var_screeninfo *var,
return offset; return offset;
} }
static void omapfb_calc_addr(const struct omapfb_info *ofbi,
const struct fb_var_screeninfo *var,
const struct fb_fix_screeninfo *fix,
int rotation, u32 *paddr, void __iomem **vaddr)
{
u32 data_start_p;
void __iomem *data_start_v;
int offset;
if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB) {
data_start_p = omapfb_get_region_rot_paddr(ofbi, rotation);
data_start_v = NULL;
} else {
data_start_p = omapfb_get_region_paddr(ofbi);
data_start_v = omapfb_get_region_vaddr(ofbi);
}
if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB)
offset = calc_rotation_offset_vrfb(var, fix, rotation);
else
offset = calc_rotation_offset_dma(var, fix, rotation);
data_start_p += offset;
data_start_v += offset;
if (offset)
DBG("offset %d, %d = %d\n",
var->xoffset, var->yoffset, offset);
DBG("paddr %x, vaddr %p\n", data_start_p, data_start_v);
*paddr = data_start_p;
*vaddr = data_start_v;
}
/* setup overlay according to the fb */ /* setup overlay according to the fb */
static int omapfb_setup_overlay(struct fb_info *fbi, struct omap_overlay *ovl, int omapfb_setup_overlay(struct fb_info *fbi, struct omap_overlay *ovl,
u16 posx, u16 posy, u16 outw, u16 outh) u16 posx, u16 posy, u16 outw, u16 outh)
{ {
int r = 0; int r = 0;
...@@ -832,9 +866,8 @@ static int omapfb_setup_overlay(struct fb_info *fbi, struct omap_overlay *ovl, ...@@ -832,9 +866,8 @@ static int omapfb_setup_overlay(struct fb_info *fbi, struct omap_overlay *ovl,
struct fb_var_screeninfo *var = &fbi->var; struct fb_var_screeninfo *var = &fbi->var;
struct fb_fix_screeninfo *fix = &fbi->fix; struct fb_fix_screeninfo *fix = &fbi->fix;
enum omap_color_mode mode = 0; enum omap_color_mode mode = 0;
int offset; u32 data_start_p = 0;
u32 data_start_p; void __iomem *data_start_v = NULL;
void __iomem *data_start_v;
struct omap_overlay_info info; struct omap_overlay_info info;
int xres, yres; int xres, yres;
int screen_width; int screen_width;
...@@ -842,6 +875,8 @@ static int omapfb_setup_overlay(struct fb_info *fbi, struct omap_overlay *ovl, ...@@ -842,6 +875,8 @@ static int omapfb_setup_overlay(struct fb_info *fbi, struct omap_overlay *ovl,
int rotation = var->rotate; int rotation = var->rotate;
int i; int i;
WARN_ON(!atomic_read(&ofbi->region->lock_count));
for (i = 0; i < ofbi->num_overlays; i++) { for (i = 0; i < ofbi->num_overlays; i++) {
if (ovl != ofbi->overlays[i]) if (ovl != ofbi->overlays[i])
continue; continue;
...@@ -861,28 +896,9 @@ static int omapfb_setup_overlay(struct fb_info *fbi, struct omap_overlay *ovl, ...@@ -861,28 +896,9 @@ static int omapfb_setup_overlay(struct fb_info *fbi, struct omap_overlay *ovl,
yres = var->yres; yres = var->yres;
} }
if (ofbi->region->size)
if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB) { omapfb_calc_addr(ofbi, var, fix, rotation,
data_start_p = omapfb_get_region_rot_paddr(ofbi, rotation); &data_start_p, &data_start_v);
data_start_v = NULL;
} else {
data_start_p = omapfb_get_region_paddr(ofbi);
data_start_v = omapfb_get_region_vaddr(ofbi);
}
if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB)
offset = calc_rotation_offset_vrfb(var, fix, rotation);
else
offset = calc_rotation_offset_dma(var, fix, rotation);
data_start_p += offset;
data_start_v += offset;
if (offset)
DBG("offset %d, %d = %d\n",
var->xoffset, var->yoffset, offset);
DBG("paddr %x, vaddr %p\n", data_start_p, data_start_v);
r = fb_mode_to_dss_mode(var, &mode); r = fb_mode_to_dss_mode(var, &mode);
if (r) { if (r) {
...@@ -954,12 +970,14 @@ int omapfb_apply_changes(struct fb_info *fbi, int init) ...@@ -954,12 +970,14 @@ int omapfb_apply_changes(struct fb_info *fbi, int init)
fill_fb(fbi); fill_fb(fbi);
#endif #endif
WARN_ON(!atomic_read(&ofbi->region->lock_count));
for (i = 0; i < ofbi->num_overlays; i++) { for (i = 0; i < ofbi->num_overlays; i++) {
ovl = ofbi->overlays[i]; ovl = ofbi->overlays[i];
DBG("apply_changes, fb %d, ovl %d\n", ofbi->id, ovl->id); DBG("apply_changes, fb %d, ovl %d\n", ofbi->id, ovl->id);
if (ofbi->region.size == 0) { if (ofbi->region->size == 0) {
/* the fb is not available. disable the overlay */ /* the fb is not available. disable the overlay */
omapfb_overlay_enable(ovl, 0); omapfb_overlay_enable(ovl, 0);
if (!init && ovl->manager) if (!init && ovl->manager)
...@@ -1007,36 +1025,48 @@ int omapfb_apply_changes(struct fb_info *fbi, int init) ...@@ -1007,36 +1025,48 @@ int omapfb_apply_changes(struct fb_info *fbi, int init)
* DO NOT MODIFY PAR */ * DO NOT MODIFY PAR */
static int omapfb_check_var(struct fb_var_screeninfo *var, struct fb_info *fbi) static int omapfb_check_var(struct fb_var_screeninfo *var, struct fb_info *fbi)
{ {
struct omapfb_info *ofbi = FB2OFB(fbi);
int r; int r;
DBG("check_var(%d)\n", FB2OFB(fbi)->id); DBG("check_var(%d)\n", FB2OFB(fbi)->id);
omapfb_get_mem_region(ofbi->region);
r = check_fb_var(fbi, var); r = check_fb_var(fbi, var);
omapfb_put_mem_region(ofbi->region);
return r; return r;
} }
/* set the video mode according to info->var */ /* set the video mode according to info->var */
static int omapfb_set_par(struct fb_info *fbi) static int omapfb_set_par(struct fb_info *fbi)
{ {
struct omapfb_info *ofbi = FB2OFB(fbi);
int r; int r;
DBG("set_par(%d)\n", FB2OFB(fbi)->id); DBG("set_par(%d)\n", FB2OFB(fbi)->id);
omapfb_get_mem_region(ofbi->region);
set_fb_fix(fbi); set_fb_fix(fbi);
r = setup_vrfb_rotation(fbi); r = setup_vrfb_rotation(fbi);
if (r) if (r)
return r; goto out;
r = omapfb_apply_changes(fbi, 0); r = omapfb_apply_changes(fbi, 0);
out:
omapfb_put_mem_region(ofbi->region);
return r; return r;
} }
static int omapfb_pan_display(struct fb_var_screeninfo *var, static int omapfb_pan_display(struct fb_var_screeninfo *var,
struct fb_info *fbi) struct fb_info *fbi)
{ {
struct omapfb_info *ofbi = FB2OFB(fbi);
struct fb_var_screeninfo new_var; struct fb_var_screeninfo new_var;
int r; int r;
...@@ -1052,23 +1082,31 @@ static int omapfb_pan_display(struct fb_var_screeninfo *var, ...@@ -1052,23 +1082,31 @@ static int omapfb_pan_display(struct fb_var_screeninfo *var,
fbi->var = new_var; fbi->var = new_var;
omapfb_get_mem_region(ofbi->region);
r = omapfb_apply_changes(fbi, 0); r = omapfb_apply_changes(fbi, 0);
omapfb_put_mem_region(ofbi->region);
return r; return r;
} }
static void mmap_user_open(struct vm_area_struct *vma) static void mmap_user_open(struct vm_area_struct *vma)
{ {
struct omapfb_info *ofbi = (struct omapfb_info *)vma->vm_private_data; struct omapfb2_mem_region *rg = vma->vm_private_data;
atomic_inc(&ofbi->map_count); omapfb_get_mem_region(rg);
atomic_inc(&rg->map_count);
omapfb_put_mem_region(rg);
} }
static void mmap_user_close(struct vm_area_struct *vma) static void mmap_user_close(struct vm_area_struct *vma)
{ {
struct omapfb_info *ofbi = (struct omapfb_info *)vma->vm_private_data; struct omapfb2_mem_region *rg = vma->vm_private_data;
atomic_dec(&ofbi->map_count); omapfb_get_mem_region(rg);
atomic_dec(&rg->map_count);
omapfb_put_mem_region(rg);
} }
static struct vm_operations_struct mmap_user_ops = { static struct vm_operations_struct mmap_user_ops = {
...@@ -1080,9 +1118,11 @@ static int omapfb_mmap(struct fb_info *fbi, struct vm_area_struct *vma) ...@@ -1080,9 +1118,11 @@ static int omapfb_mmap(struct fb_info *fbi, struct vm_area_struct *vma)
{ {
struct omapfb_info *ofbi = FB2OFB(fbi); struct omapfb_info *ofbi = FB2OFB(fbi);
struct fb_fix_screeninfo *fix = &fbi->fix; struct fb_fix_screeninfo *fix = &fbi->fix;
struct omapfb2_mem_region *rg;
unsigned long off; unsigned long off;
unsigned long start; unsigned long start;
u32 len; u32 len;
int r = -EINVAL;
if (vma->vm_end - vma->vm_start == 0) if (vma->vm_end - vma->vm_start == 0)
return 0; return 0;
...@@ -1090,12 +1130,14 @@ static int omapfb_mmap(struct fb_info *fbi, struct vm_area_struct *vma) ...@@ -1090,12 +1130,14 @@ static int omapfb_mmap(struct fb_info *fbi, struct vm_area_struct *vma)
return -EINVAL; return -EINVAL;
off = vma->vm_pgoff << PAGE_SHIFT; off = vma->vm_pgoff << PAGE_SHIFT;
rg = omapfb_get_mem_region(ofbi->region);
start = omapfb_get_region_paddr(ofbi); start = omapfb_get_region_paddr(ofbi);
len = fix->smem_len; len = fix->smem_len;
if (off >= len) if (off >= len)
return -EINVAL; goto error;
if ((vma->vm_end - vma->vm_start + off) > len) if ((vma->vm_end - vma->vm_start + off) > len)
return -EINVAL; goto error;
off += start; off += start;
...@@ -1105,13 +1147,25 @@ static int omapfb_mmap(struct fb_info *fbi, struct vm_area_struct *vma) ...@@ -1105,13 +1147,25 @@ static int omapfb_mmap(struct fb_info *fbi, struct vm_area_struct *vma)
vma->vm_flags |= VM_IO | VM_RESERVED; vma->vm_flags |= VM_IO | VM_RESERVED;
vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot); vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
vma->vm_ops = &mmap_user_ops; vma->vm_ops = &mmap_user_ops;
vma->vm_private_data = ofbi; vma->vm_private_data = rg;
if (io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT, if (io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT,
vma->vm_end - vma->vm_start, vma->vm_page_prot)) vma->vm_end - vma->vm_start,
return -EAGAIN; vma->vm_page_prot)) {
r = -EAGAIN;
goto error;
}
/* vm_ops.open won't be called for mmap itself. */ /* vm_ops.open won't be called for mmap itself. */
atomic_inc(&ofbi->map_count); atomic_inc(&rg->map_count);
omapfb_put_mem_region(rg);
return 0; return 0;
error:
omapfb_put_mem_region(ofbi->region);
return r;
} }
/* Store a single color palette entry into a pseudo palette or the hardware /* Store a single color palette entry into a pseudo palette or the hardware
...@@ -1154,11 +1208,6 @@ static int _setcolreg(struct fb_info *fbi, u_int regno, u_int red, u_int green, ...@@ -1154,11 +1208,6 @@ static int _setcolreg(struct fb_info *fbi, u_int regno, u_int red, u_int green,
if (r != 0) if (r != 0)
break; break;
if (regno < 0) {
r = -EINVAL;
break;
}
if (regno < 16) { if (regno < 16) {
u16 pal; u16 pal;
pal = ((red >> (16 - var->red.length)) << pal = ((red >> (16 - var->red.length)) <<
...@@ -1217,6 +1266,9 @@ static int omapfb_blank(int blank, struct fb_info *fbi) ...@@ -1217,6 +1266,9 @@ static int omapfb_blank(int blank, struct fb_info *fbi)
int do_update = 0; int do_update = 0;
int r = 0; int r = 0;
if (!display)
return -EINVAL;
omapfb_lock(fbdev); omapfb_lock(fbdev);
switch (blank) { switch (blank) {
...@@ -1300,7 +1352,9 @@ static void omapfb_free_fbmem(struct fb_info *fbi) ...@@ -1300,7 +1352,9 @@ static void omapfb_free_fbmem(struct fb_info *fbi)
struct omapfb2_device *fbdev = ofbi->fbdev; struct omapfb2_device *fbdev = ofbi->fbdev;
struct omapfb2_mem_region *rg; struct omapfb2_mem_region *rg;
rg = &ofbi->region; rg = ofbi->region;
WARN_ON(atomic_read(&rg->map_count));
if (rg->paddr) if (rg->paddr)
if (omap_vram_free(rg->paddr, rg->size)) if (omap_vram_free(rg->paddr, rg->size))
...@@ -1355,8 +1409,15 @@ static int omapfb_alloc_fbmem(struct fb_info *fbi, unsigned long size, ...@@ -1355,8 +1409,15 @@ static int omapfb_alloc_fbmem(struct fb_info *fbi, unsigned long size,
void __iomem *vaddr; void __iomem *vaddr;
int r; int r;
rg = &ofbi->region; rg = ofbi->region;
memset(rg, 0, sizeof(*rg));
rg->paddr = 0;
rg->vaddr = NULL;
memset(&rg->vrfb, 0, sizeof rg->vrfb);
rg->size = 0;
rg->type = 0;
rg->alloc = false;
rg->map = false;
size = PAGE_ALIGN(size); size = PAGE_ALIGN(size);
...@@ -1609,7 +1670,7 @@ static int omapfb_allocate_all_fbs(struct omapfb2_device *fbdev) ...@@ -1609,7 +1670,7 @@ static int omapfb_allocate_all_fbs(struct omapfb2_device *fbdev)
for (i = 0; i < fbdev->num_fbs; i++) { for (i = 0; i < fbdev->num_fbs; i++) {
struct omapfb_info *ofbi = FB2OFB(fbdev->fbs[i]); struct omapfb_info *ofbi = FB2OFB(fbdev->fbs[i]);
struct omapfb2_mem_region *rg; struct omapfb2_mem_region *rg;
rg = &ofbi->region; rg = ofbi->region;
DBG("region%d phys %08x virt %p size=%lu\n", DBG("region%d phys %08x virt %p size=%lu\n",
i, i,
...@@ -1626,7 +1687,7 @@ int omapfb_realloc_fbmem(struct fb_info *fbi, unsigned long size, int type) ...@@ -1626,7 +1687,7 @@ int omapfb_realloc_fbmem(struct fb_info *fbi, unsigned long size, int type)
struct omapfb_info *ofbi = FB2OFB(fbi); struct omapfb_info *ofbi = FB2OFB(fbi);
struct omapfb2_device *fbdev = ofbi->fbdev; struct omapfb2_device *fbdev = ofbi->fbdev;
struct omap_dss_device *display = fb2display(fbi); struct omap_dss_device *display = fb2display(fbi);
struct omapfb2_mem_region *rg = &ofbi->region; struct omapfb2_mem_region *rg = ofbi->region;
unsigned long old_size = rg->size; unsigned long old_size = rg->size;
unsigned long old_paddr = rg->paddr; unsigned long old_paddr = rg->paddr;
int old_type = rg->type; int old_type = rg->type;
...@@ -1709,7 +1770,7 @@ static int omapfb_fb_init(struct omapfb2_device *fbdev, struct fb_info *fbi) ...@@ -1709,7 +1770,7 @@ static int omapfb_fb_init(struct omapfb2_device *fbdev, struct fb_info *fbi)
fbi->flags = FBINFO_FLAG_DEFAULT; fbi->flags = FBINFO_FLAG_DEFAULT;
fbi->pseudo_palette = fbdev->pseudo_palette; fbi->pseudo_palette = fbdev->pseudo_palette;
if (ofbi->region.size == 0) { if (ofbi->region->size == 0) {
clear_fb_info(fbi); clear_fb_info(fbi);
return 0; return 0;
} }
...@@ -1871,6 +1932,10 @@ static int omapfb_create_framebuffers(struct omapfb2_device *fbdev) ...@@ -1871,6 +1932,10 @@ static int omapfb_create_framebuffers(struct omapfb2_device *fbdev)
ofbi->fbdev = fbdev; ofbi->fbdev = fbdev;
ofbi->id = i; ofbi->id = i;
ofbi->region = &fbdev->regions[i];
ofbi->region->id = i;
init_rwsem(&ofbi->region->lock);
/* assign these early, so that fb alloc can use them */ /* assign these early, so that fb alloc can use them */
ofbi->rotation_type = def_vrfb ? OMAP_DSS_ROT_VRFB : ofbi->rotation_type = def_vrfb ? OMAP_DSS_ROT_VRFB :
OMAP_DSS_ROT_DMA; OMAP_DSS_ROT_DMA;
...@@ -1900,7 +1965,13 @@ static int omapfb_create_framebuffers(struct omapfb2_device *fbdev) ...@@ -1900,7 +1965,13 @@ static int omapfb_create_framebuffers(struct omapfb2_device *fbdev)
/* setup fb_infos */ /* setup fb_infos */
for (i = 0; i < fbdev->num_fbs; i++) { for (i = 0; i < fbdev->num_fbs; i++) {
r = omapfb_fb_init(fbdev, fbdev->fbs[i]); struct fb_info *fbi = fbdev->fbs[i];
struct omapfb_info *ofbi = FB2OFB(fbi);
omapfb_get_mem_region(ofbi->region);
r = omapfb_fb_init(fbdev, fbi);
omapfb_put_mem_region(ofbi->region);
if (r) { if (r) {
dev_err(fbdev->dev, "failed to setup fb_info\n"); dev_err(fbdev->dev, "failed to setup fb_info\n");
return r; return r;
...@@ -1921,20 +1992,19 @@ static int omapfb_create_framebuffers(struct omapfb2_device *fbdev) ...@@ -1921,20 +1992,19 @@ static int omapfb_create_framebuffers(struct omapfb2_device *fbdev)
DBG("framebuffers registered\n"); DBG("framebuffers registered\n");
for (i = 0; i < fbdev->num_fbs; i++) { for (i = 0; i < fbdev->num_fbs; i++) {
r = omapfb_apply_changes(fbdev->fbs[i], 1); struct fb_info *fbi = fbdev->fbs[i];
struct omapfb_info *ofbi = FB2OFB(fbi);
omapfb_get_mem_region(ofbi->region);
r = omapfb_apply_changes(fbi, 1);
omapfb_put_mem_region(ofbi->region);
if (r) { if (r) {
dev_err(fbdev->dev, "failed to change mode\n"); dev_err(fbdev->dev, "failed to change mode\n");
return r; return r;
} }
} }
DBG("create sysfs for fbs\n");
r = omapfb_create_sysfs(fbdev);
if (r) {
dev_err(fbdev->dev, "failed to create sysfs entries\n");
return r;
}
/* Enable fb0 */ /* Enable fb0 */
if (fbdev->num_fbs > 0) { if (fbdev->num_fbs > 0) {
struct omapfb_info *ofbi = FB2OFB(fbdev->fbs[0]); struct omapfb_info *ofbi = FB2OFB(fbdev->fbs[0]);
...@@ -1968,11 +2038,11 @@ static int omapfb_mode_to_timings(const char *mode_str, ...@@ -1968,11 +2038,11 @@ static int omapfb_mode_to_timings(const char *mode_str,
#ifdef CONFIG_OMAP2_DSS_VENC #ifdef CONFIG_OMAP2_DSS_VENC
if (strcmp(mode_str, "pal") == 0) { if (strcmp(mode_str, "pal") == 0) {
*timings = omap_dss_pal_timings; *timings = omap_dss_pal_timings;
*bpp = 0; *bpp = 24;
return 0; return 0;
} else if (strcmp(mode_str, "ntsc") == 0) { } else if (strcmp(mode_str, "ntsc") == 0) {
*timings = omap_dss_ntsc_timings; *timings = omap_dss_ntsc_timings;
*bpp = 0; *bpp = 24;
return 0; return 0;
} }
#endif #endif
...@@ -2220,6 +2290,13 @@ static int omapfb_probe(struct platform_device *pdev) ...@@ -2220,6 +2290,13 @@ static int omapfb_probe(struct platform_device *pdev)
} }
} }
DBG("create sysfs for fbs\n");
r = omapfb_create_sysfs(fbdev);
if (r) {
dev_err(fbdev->dev, "failed to create sysfs entries\n");
goto cleanup;
}
return 0; return 0;
cleanup: cleanup:
......
...@@ -49,6 +49,7 @@ static ssize_t store_rotate_type(struct device *dev, ...@@ -49,6 +49,7 @@ static ssize_t store_rotate_type(struct device *dev,
{ {
struct fb_info *fbi = dev_get_drvdata(dev); struct fb_info *fbi = dev_get_drvdata(dev);
struct omapfb_info *ofbi = FB2OFB(fbi); struct omapfb_info *ofbi = FB2OFB(fbi);
struct omapfb2_mem_region *rg;
enum omap_dss_rotation_type rot_type; enum omap_dss_rotation_type rot_type;
int r; int r;
...@@ -64,9 +65,11 @@ static ssize_t store_rotate_type(struct device *dev, ...@@ -64,9 +65,11 @@ static ssize_t store_rotate_type(struct device *dev,
if (rot_type == ofbi->rotation_type) if (rot_type == ofbi->rotation_type)
goto out; goto out;
if (ofbi->region.size) { rg = omapfb_get_mem_region(ofbi->region);
if (rg->size) {
r = -EBUSY; r = -EBUSY;
goto out; goto put_region;
} }
ofbi->rotation_type = rot_type; ofbi->rotation_type = rot_type;
...@@ -75,6 +78,8 @@ static ssize_t store_rotate_type(struct device *dev, ...@@ -75,6 +78,8 @@ static ssize_t store_rotate_type(struct device *dev,
* Since the VRAM for this FB is not allocated at the moment we don't * Since the VRAM for this FB is not allocated at the moment we don't
* need to do any further parameter checking at this point. * need to do any further parameter checking at this point.
*/ */
put_region:
omapfb_put_mem_region(rg);
out: out:
unlock_fb_info(fbi); unlock_fb_info(fbi);
...@@ -97,7 +102,7 @@ static ssize_t store_mirror(struct device *dev, ...@@ -97,7 +102,7 @@ static ssize_t store_mirror(struct device *dev,
{ {
struct fb_info *fbi = dev_get_drvdata(dev); struct fb_info *fbi = dev_get_drvdata(dev);
struct omapfb_info *ofbi = FB2OFB(fbi); struct omapfb_info *ofbi = FB2OFB(fbi);
bool mirror; unsigned long mirror;
int r; int r;
struct fb_var_screeninfo new_var; struct fb_var_screeninfo new_var;
...@@ -111,6 +116,8 @@ static ssize_t store_mirror(struct device *dev, ...@@ -111,6 +116,8 @@ static ssize_t store_mirror(struct device *dev,
ofbi->mirror = mirror; ofbi->mirror = mirror;
omapfb_get_mem_region(ofbi->region);
memcpy(&new_var, &fbi->var, sizeof(new_var)); memcpy(&new_var, &fbi->var, sizeof(new_var));
r = check_fb_var(fbi, &new_var); r = check_fb_var(fbi, &new_var);
if (r) if (r)
...@@ -125,6 +132,8 @@ static ssize_t store_mirror(struct device *dev, ...@@ -125,6 +132,8 @@ static ssize_t store_mirror(struct device *dev,
r = count; r = count;
out: out:
omapfb_put_mem_region(ofbi->region);
unlock_fb_info(fbi); unlock_fb_info(fbi);
return r; return r;
...@@ -263,11 +272,15 @@ static ssize_t store_overlays(struct device *dev, struct device_attribute *attr, ...@@ -263,11 +272,15 @@ static ssize_t store_overlays(struct device *dev, struct device_attribute *attr,
DBG("detaching %d\n", ofbi->overlays[i]->id); DBG("detaching %d\n", ofbi->overlays[i]->id);
omapfb_get_mem_region(ofbi->region);
omapfb_overlay_enable(ovl, 0); omapfb_overlay_enable(ovl, 0);
if (ovl->manager) if (ovl->manager)
ovl->manager->apply(ovl->manager); ovl->manager->apply(ovl->manager);
omapfb_put_mem_region(ofbi->region);
for (t = i + 1; t < ofbi->num_overlays; t++) { for (t = i + 1; t < ofbi->num_overlays; t++) {
ofbi->rotation[t-1] = ofbi->rotation[t]; ofbi->rotation[t-1] = ofbi->rotation[t];
ofbi->overlays[t-1] = ofbi->overlays[t]; ofbi->overlays[t-1] = ofbi->overlays[t];
...@@ -300,7 +313,12 @@ static ssize_t store_overlays(struct device *dev, struct device_attribute *attr, ...@@ -300,7 +313,12 @@ static ssize_t store_overlays(struct device *dev, struct device_attribute *attr,
} }
if (added) { if (added) {
omapfb_get_mem_region(ofbi->region);
r = omapfb_apply_changes(fbi, 0); r = omapfb_apply_changes(fbi, 0);
omapfb_put_mem_region(ofbi->region);
if (r) if (r)
goto out; goto out;
} }
...@@ -388,7 +406,12 @@ static ssize_t store_overlays_rotate(struct device *dev, ...@@ -388,7 +406,12 @@ static ssize_t store_overlays_rotate(struct device *dev,
for (i = 0; i < num_ovls; ++i) for (i = 0; i < num_ovls; ++i)
ofbi->rotation[i] = rotation[i]; ofbi->rotation[i] = rotation[i];
omapfb_get_mem_region(ofbi->region);
r = omapfb_apply_changes(fbi, 0); r = omapfb_apply_changes(fbi, 0);
omapfb_put_mem_region(ofbi->region);
if (r) if (r)
goto out; goto out;
...@@ -408,7 +431,7 @@ static ssize_t show_size(struct device *dev, ...@@ -408,7 +431,7 @@ static ssize_t show_size(struct device *dev,
struct fb_info *fbi = dev_get_drvdata(dev); struct fb_info *fbi = dev_get_drvdata(dev);
struct omapfb_info *ofbi = FB2OFB(fbi); struct omapfb_info *ofbi = FB2OFB(fbi);
return snprintf(buf, PAGE_SIZE, "%lu\n", ofbi->region.size); return snprintf(buf, PAGE_SIZE, "%lu\n", ofbi->region->size);
} }
static ssize_t store_size(struct device *dev, struct device_attribute *attr, static ssize_t store_size(struct device *dev, struct device_attribute *attr,
...@@ -416,6 +439,8 @@ static ssize_t store_size(struct device *dev, struct device_attribute *attr, ...@@ -416,6 +439,8 @@ static ssize_t store_size(struct device *dev, struct device_attribute *attr,
{ {
struct fb_info *fbi = dev_get_drvdata(dev); struct fb_info *fbi = dev_get_drvdata(dev);
struct omapfb_info *ofbi = FB2OFB(fbi); struct omapfb_info *ofbi = FB2OFB(fbi);
struct omapfb2_device *fbdev = ofbi->fbdev;
struct omapfb2_mem_region *rg;
unsigned long size; unsigned long size;
int r; int r;
int i; int i;
...@@ -425,15 +450,33 @@ static ssize_t store_size(struct device *dev, struct device_attribute *attr, ...@@ -425,15 +450,33 @@ static ssize_t store_size(struct device *dev, struct device_attribute *attr,
if (!lock_fb_info(fbi)) if (!lock_fb_info(fbi))
return -ENODEV; return -ENODEV;
for (i = 0; i < ofbi->num_overlays; i++) { rg = ofbi->region;
if (ofbi->overlays[i]->info.enabled) {
r = -EBUSY; down_write_nested(&rg->lock, rg->id);
goto out; atomic_inc(&rg->lock_count);
if (atomic_read(&rg->map_count)) {
r = -EBUSY;
goto out;
}
for (i = 0; i < fbdev->num_fbs; i++) {
struct omapfb_info *ofbi2 = FB2OFB(fbdev->fbs[i]);
int j;
if (ofbi2->region != rg)
continue;
for (j = 0; j < ofbi2->num_overlays; j++) {
if (ofbi2->overlays[j]->info.enabled) {
r = -EBUSY;
goto out;
}
} }
} }
if (size != ofbi->region.size) { if (size != ofbi->region->size) {
r = omapfb_realloc_fbmem(fbi, size, ofbi->region.type); r = omapfb_realloc_fbmem(fbi, size, ofbi->region->type);
if (r) { if (r) {
dev_err(dev, "realloc fbmem failed\n"); dev_err(dev, "realloc fbmem failed\n");
goto out; goto out;
...@@ -442,6 +485,9 @@ static ssize_t store_size(struct device *dev, struct device_attribute *attr, ...@@ -442,6 +485,9 @@ static ssize_t store_size(struct device *dev, struct device_attribute *attr,
r = count; r = count;
out: out:
atomic_dec(&rg->lock_count);
up_write(&rg->lock);
unlock_fb_info(fbi); unlock_fb_info(fbi);
return r; return r;
...@@ -453,7 +499,7 @@ static ssize_t show_phys(struct device *dev, ...@@ -453,7 +499,7 @@ static ssize_t show_phys(struct device *dev,
struct fb_info *fbi = dev_get_drvdata(dev); struct fb_info *fbi = dev_get_drvdata(dev);
struct omapfb_info *ofbi = FB2OFB(fbi); struct omapfb_info *ofbi = FB2OFB(fbi);
return snprintf(buf, PAGE_SIZE, "%0x\n", ofbi->region.paddr); return snprintf(buf, PAGE_SIZE, "%0x\n", ofbi->region->paddr);
} }
static ssize_t show_virt(struct device *dev, static ssize_t show_virt(struct device *dev,
...@@ -462,7 +508,7 @@ static ssize_t show_virt(struct device *dev, ...@@ -462,7 +508,7 @@ static ssize_t show_virt(struct device *dev,
struct fb_info *fbi = dev_get_drvdata(dev); struct fb_info *fbi = dev_get_drvdata(dev);
struct omapfb_info *ofbi = FB2OFB(fbi); struct omapfb_info *ofbi = FB2OFB(fbi);
return snprintf(buf, PAGE_SIZE, "%p\n", ofbi->region.vaddr); return snprintf(buf, PAGE_SIZE, "%p\n", ofbi->region->vaddr);
} }
static struct device_attribute omapfb_attrs[] = { static struct device_attribute omapfb_attrs[] = {
......
...@@ -27,6 +27,8 @@ ...@@ -27,6 +27,8 @@
#define DEBUG #define DEBUG
#endif #endif
#include <linux/rwsem.h>
#include <plat/display.h> #include <plat/display.h>
#ifdef DEBUG #ifdef DEBUG
...@@ -44,6 +46,7 @@ extern unsigned int omapfb_debug; ...@@ -44,6 +46,7 @@ extern unsigned int omapfb_debug;
#define OMAPFB_MAX_OVL_PER_FB 3 #define OMAPFB_MAX_OVL_PER_FB 3
struct omapfb2_mem_region { struct omapfb2_mem_region {
int id;
u32 paddr; u32 paddr;
void __iomem *vaddr; void __iomem *vaddr;
struct vrfb vrfb; struct vrfb vrfb;
...@@ -51,13 +54,15 @@ struct omapfb2_mem_region { ...@@ -51,13 +54,15 @@ struct omapfb2_mem_region {
u8 type; /* OMAPFB_PLANE_MEM_* */ u8 type; /* OMAPFB_PLANE_MEM_* */
bool alloc; /* allocated by the driver */ bool alloc; /* allocated by the driver */
bool map; /* kernel mapped by the driver */ bool map; /* kernel mapped by the driver */
atomic_t map_count;
struct rw_semaphore lock;
atomic_t lock_count;
}; };
/* appended to fb_info */ /* appended to fb_info */
struct omapfb_info { struct omapfb_info {
int id; int id;
struct omapfb2_mem_region region; struct omapfb2_mem_region *region;
atomic_t map_count;
int num_overlays; int num_overlays;
struct omap_overlay *overlays[OMAPFB_MAX_OVL_PER_FB]; struct omap_overlay *overlays[OMAPFB_MAX_OVL_PER_FB];
struct omapfb2_device *fbdev; struct omapfb2_device *fbdev;
...@@ -76,6 +81,7 @@ struct omapfb2_device { ...@@ -76,6 +81,7 @@ struct omapfb2_device {
unsigned num_fbs; unsigned num_fbs;
struct fb_info *fbs[10]; struct fb_info *fbs[10];
struct omapfb2_mem_region regions[10];
unsigned num_displays; unsigned num_displays;
struct omap_dss_device *displays[10]; struct omap_dss_device *displays[10];
...@@ -117,6 +123,9 @@ int omapfb_update_window(struct fb_info *fbi, ...@@ -117,6 +123,9 @@ int omapfb_update_window(struct fb_info *fbi,
int dss_mode_to_fb_mode(enum omap_color_mode dssmode, int dss_mode_to_fb_mode(enum omap_color_mode dssmode,
struct fb_var_screeninfo *var); struct fb_var_screeninfo *var);
int omapfb_setup_overlay(struct fb_info *fbi, struct omap_overlay *ovl,
u16 posx, u16 posy, u16 outw, u16 outh);
/* find the display connected to this fb, if any */ /* find the display connected to this fb, if any */
static inline struct omap_dss_device *fb2display(struct fb_info *fbi) static inline struct omap_dss_device *fb2display(struct fb_info *fbi)
{ {
...@@ -148,8 +157,24 @@ static inline int omapfb_overlay_enable(struct omap_overlay *ovl, ...@@ -148,8 +157,24 @@ static inline int omapfb_overlay_enable(struct omap_overlay *ovl,
struct omap_overlay_info info; struct omap_overlay_info info;
ovl->get_overlay_info(ovl, &info); ovl->get_overlay_info(ovl, &info);
if (info.enabled == enable)
return 0;
info.enabled = enable; info.enabled = enable;
return ovl->set_overlay_info(ovl, &info); return ovl->set_overlay_info(ovl, &info);
} }
static inline struct omapfb2_mem_region *
omapfb_get_mem_region(struct omapfb2_mem_region *rg)
{
down_read_nested(&rg->lock, rg->id);
atomic_inc(&rg->lock_count);
return rg;
}
static inline void omapfb_put_mem_region(struct omapfb2_mem_region *rg)
{
atomic_dec(&rg->lock_count);
up_read(&rg->lock);
}
#endif #endif
...@@ -85,6 +85,9 @@ ...@@ -85,6 +85,9 @@
#define OMAPFB_MEMTYPE_SRAM 1 #define OMAPFB_MEMTYPE_SRAM 1
#define OMAPFB_MEMTYPE_MAX 1 #define OMAPFB_MEMTYPE_MAX 1
#define OMAPFB_MEM_IDX_ENABLED 0x80
#define OMAPFB_MEM_IDX_MASK 0x7f
enum omapfb_color_format { enum omapfb_color_format {
OMAPFB_COLOR_RGB565 = 0, OMAPFB_COLOR_RGB565 = 0,
OMAPFB_COLOR_YUV422, OMAPFB_COLOR_YUV422,
...@@ -136,7 +139,7 @@ struct omapfb_plane_info { ...@@ -136,7 +139,7 @@ struct omapfb_plane_info {
__u8 enabled; __u8 enabled;
__u8 channel_out; __u8 channel_out;
__u8 mirror; __u8 mirror;
__u8 reserved1; __u8 mem_idx;
__u32 out_width; __u32 out_width;
__u32 out_height; __u32 out_height;
__u32 reserved2[12]; __u32 reserved2[12];
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment