Commit a5a0fc67 authored by Dave Airlie's avatar Dave Airlie

Merge branch 'exynos-drm-next' of git://git.infradead.org/users/kmpark/linux-samsung into drm-next

Inki writes:
"this patch set updates exynos drm framework and includes minor fixups.
and this pull request except hdmi device tree support patch set posted
by Rahul Sharma because that includes media side patch so for this
patch set, we may have git pull one more time in addition, if we get
an agreement with media guys. for this patch, you can refer to below link,
        http://comments.gmane.org/gmane.comp.video.dri.devel/74504

 this pull request adds hdmi device tree support
and includes related patch set such as disabling of hdmi internal
interrupt, suppport for platform variants for hdmi and mixer,
support to disable video processor based on platform type and
removal of drm common platform data. as you know, this patch
set was delayed because it included an media side patch. so for this,
we got an ack from v4l2-based hdmi driver author, Tomasz Stanislawski."

* 'exynos-drm-next' of git://git.infradead.org/users/kmpark/linux-samsung: (34 commits)
  drm: exynos: hdmi: remove drm common hdmi platform data struct
  drm: exynos: hdmi: add support for exynos5 hdmi
  drm: exynos: hdmi: replace is_v13 with version check in hdmi
  drm: exynos: hdmi: add support for exynos5 mixer
  drm: exynos: hdmi: add support to disable video processor in mixer
  drm: exynos: hdmi: add support for platform variants for mixer
  drm: exynos: hdmi: add support for exynos5 hdmiphy
  drm: exynos: hdmi: add support for exynos5 ddc
  drm: exynos: remove drm hdmi platform data struct
  drm: exynos: hdmi: turn off HPD interrupt in HDMI chip
  drm: exynos: hdmi: use s5p-hdmi platform data
  drm: exynos: hdmi: fix interrupt handling
  drm: exynos: hdmi: support for platform variants
  media: s5p-hdmi: add HPD GPIO to platform data
  drm/exynos: fix kcalloc size of g2d cmdlist node
  drm/exynos: fix to calculate CRTC shown via screen
  drm/exynos: fix display power call issue.
  drm/exynos: add platform_device_id table and driver data for drm fimd
  drm/exynos: Fix potential NULL pointer dereference
  drm/exynos: support drm_wait_vblank feature for VIDI
  ...

Conflicts:
	include/drm/exynos_drm.h
parents 0dbe2321 768c3059
......@@ -26,29 +26,41 @@ static int s5p_ddc_probe(struct i2c_client *client,
{
hdmi_attach_ddc_client(client);
dev_info(&client->adapter->dev, "attached s5p_ddc "
"into i2c adapter successfully\n");
dev_info(&client->adapter->dev,
"attached %s into i2c adapter successfully\n",
client->name);
return 0;
}
static int s5p_ddc_remove(struct i2c_client *client)
{
dev_info(&client->adapter->dev, "detached s5p_ddc "
"from i2c adapter successfully\n");
dev_info(&client->adapter->dev,
"detached %s from i2c adapter successfully\n",
client->name);
return 0;
}
static struct i2c_device_id ddc_idtable[] = {
{"s5p_ddc", 0},
{"exynos5-hdmiddc", 0},
{ },
};
static struct of_device_id hdmiddc_match_types[] = {
{
.compatible = "samsung,exynos5-hdmiddc",
}, {
/* end node */
}
};
struct i2c_driver ddc_driver = {
.driver = {
.name = "s5p_ddc",
.name = "exynos-hdmiddc",
.owner = THIS_MODULE,
.of_match_table = hdmiddc_match_types,
},
.id_table = ddc_idtable,
.probe = s5p_ddc_probe,
......
......@@ -40,6 +40,7 @@ struct exynos_drm_connector {
struct drm_connector drm_connector;
uint32_t encoder_id;
struct exynos_drm_manager *manager;
uint32_t dpms;
};
/* convert exynos_video_timings to drm_display_mode */
......@@ -149,8 +150,12 @@ static int exynos_drm_connector_get_modes(struct drm_connector *connector)
count = drm_add_edid_modes(connector, edid);
kfree(edid);
} else {
struct drm_display_mode *mode = drm_mode_create(connector->dev);
struct exynos_drm_panel_info *panel;
struct drm_display_mode *mode = drm_mode_create(connector->dev);
if (!mode) {
DRM_ERROR("failed to create a new display mode.\n");
return 0;
}
if (display_ops->get_panel)
panel = display_ops->get_panel(manager->dev);
......@@ -194,8 +199,7 @@ static int exynos_drm_connector_mode_valid(struct drm_connector *connector,
return ret;
}
static struct drm_encoder *exynos_drm_best_encoder(
struct drm_connector *connector)
struct drm_encoder *exynos_drm_best_encoder(struct drm_connector *connector)
{
struct drm_device *dev = connector->dev;
struct exynos_drm_connector *exynos_connector =
......@@ -224,6 +228,43 @@ static struct drm_connector_helper_funcs exynos_connector_helper_funcs = {
.best_encoder = exynos_drm_best_encoder,
};
void exynos_drm_display_power(struct drm_connector *connector, int mode)
{
struct drm_encoder *encoder = exynos_drm_best_encoder(connector);
struct exynos_drm_connector *exynos_connector;
struct exynos_drm_manager *manager = exynos_drm_get_manager(encoder);
struct exynos_drm_display_ops *display_ops = manager->display_ops;
exynos_connector = to_exynos_connector(connector);
if (exynos_connector->dpms == mode) {
DRM_DEBUG_KMS("desired dpms mode is same as previous one.\n");
return;
}
if (display_ops && display_ops->power_on)
display_ops->power_on(manager->dev, mode);
exynos_connector->dpms = mode;
}
static void exynos_drm_connector_dpms(struct drm_connector *connector,
int mode)
{
DRM_DEBUG_KMS("%s\n", __FILE__);
/*
* in case that drm_crtc_helper_set_mode() is called,
* encoder/crtc->funcs->dpms() will be just returned
* because they already were DRM_MODE_DPMS_ON so only
* exynos_drm_display_power() will be called.
*/
drm_helper_connector_dpms(connector, mode);
exynos_drm_display_power(connector, mode);
}
static int exynos_drm_connector_fill_modes(struct drm_connector *connector,
unsigned int max_width, unsigned int max_height)
{
......@@ -283,7 +324,7 @@ static void exynos_drm_connector_destroy(struct drm_connector *connector)
}
static struct drm_connector_funcs exynos_connector_funcs = {
.dpms = drm_helper_connector_dpms,
.dpms = exynos_drm_connector_dpms,
.fill_modes = exynos_drm_connector_fill_modes,
.detect = exynos_drm_connector_detect,
.destroy = exynos_drm_connector_destroy,
......@@ -332,6 +373,7 @@ struct drm_connector *exynos_drm_connector_create(struct drm_device *dev,
exynos_connector->encoder_id = encoder->base.id;
exynos_connector->manager = manager;
exynos_connector->dpms = DRM_MODE_DPMS_OFF;
connector->encoder = encoder;
err = drm_mode_connector_attach_encoder(connector, encoder);
......
......@@ -31,4 +31,8 @@
struct drm_connector *exynos_drm_connector_create(struct drm_device *dev,
struct drm_encoder *encoder);
struct drm_encoder *exynos_drm_best_encoder(struct drm_connector *connector);
void exynos_drm_display_power(struct drm_connector *connector, int mode);
#endif
......@@ -34,32 +34,14 @@
static LIST_HEAD(exynos_drm_subdrv_list);
static int exynos_drm_subdrv_probe(struct drm_device *dev,
static int exynos_drm_create_enc_conn(struct drm_device *dev,
struct exynos_drm_subdrv *subdrv)
{
struct drm_encoder *encoder;
struct drm_connector *connector;
DRM_DEBUG_DRIVER("%s\n", __FILE__);
if (subdrv->probe) {
int ret;
/*
* this probe callback would be called by sub driver
* after setting of all resources to this sub driver,
* such as clock, irq and register map are done or by load()
* of exynos drm driver.
*
* P.S. note that this driver is considered for modularization.
*/
ret = subdrv->probe(dev, subdrv->dev);
if (ret)
return ret;
}
if (!subdrv->manager)
return 0;
DRM_DEBUG_DRIVER("%s\n", __FILE__);
subdrv->manager->dev = subdrv->dev;
......@@ -78,24 +60,22 @@ static int exynos_drm_subdrv_probe(struct drm_device *dev,
connector = exynos_drm_connector_create(dev, encoder);
if (!connector) {
DRM_ERROR("failed to create connector\n");
encoder->funcs->destroy(encoder);
return -EFAULT;
ret = -EFAULT;
goto err_destroy_encoder;
}
subdrv->encoder = encoder;
subdrv->connector = connector;
return 0;
err_destroy_encoder:
encoder->funcs->destroy(encoder);
return ret;
}
static void exynos_drm_subdrv_remove(struct drm_device *dev,
struct exynos_drm_subdrv *subdrv)
static void exynos_drm_destroy_enc_conn(struct exynos_drm_subdrv *subdrv)
{
DRM_DEBUG_DRIVER("%s\n", __FILE__);
if (subdrv->remove)
subdrv->remove(dev);
if (subdrv->encoder) {
struct drm_encoder *encoder = subdrv->encoder;
encoder->funcs->destroy(encoder);
......@@ -109,9 +89,43 @@ static void exynos_drm_subdrv_remove(struct drm_device *dev,
}
}
static int exynos_drm_subdrv_probe(struct drm_device *dev,
struct exynos_drm_subdrv *subdrv)
{
if (subdrv->probe) {
int ret;
subdrv->drm_dev = dev;
/*
* this probe callback would be called by sub driver
* after setting of all resources to this sub driver,
* such as clock, irq and register map are done or by load()
* of exynos drm driver.
*
* P.S. note that this driver is considered for modularization.
*/
ret = subdrv->probe(dev, subdrv->dev);
if (ret)
return ret;
}
return 0;
}
static void exynos_drm_subdrv_remove(struct drm_device *dev,
struct exynos_drm_subdrv *subdrv)
{
DRM_DEBUG_DRIVER("%s\n", __FILE__);
if (subdrv->remove)
subdrv->remove(dev, subdrv->dev);
}
int exynos_drm_device_register(struct drm_device *dev)
{
struct exynos_drm_subdrv *subdrv, *n;
unsigned int fine_cnt = 0;
int err;
DRM_DEBUG_DRIVER("%s\n", __FILE__);
......@@ -120,14 +134,36 @@ int exynos_drm_device_register(struct drm_device *dev)
return -EINVAL;
list_for_each_entry_safe(subdrv, n, &exynos_drm_subdrv_list, list) {
subdrv->drm_dev = dev;
err = exynos_drm_subdrv_probe(dev, subdrv);
if (err) {
DRM_DEBUG("exynos drm subdrv probe failed.\n");
list_del(&subdrv->list);
continue;
}
/*
* if manager is null then it means that this sub driver
* doesn't need encoder and connector.
*/
if (!subdrv->manager) {
fine_cnt++;
continue;
}
err = exynos_drm_create_enc_conn(dev, subdrv);
if (err) {
DRM_DEBUG("failed to create encoder and connector.\n");
exynos_drm_subdrv_remove(dev, subdrv);
list_del(&subdrv->list);
continue;
}
fine_cnt++;
}
if (!fine_cnt)
return -EINVAL;
return 0;
}
EXPORT_SYMBOL_GPL(exynos_drm_device_register);
......@@ -143,8 +179,10 @@ int exynos_drm_device_unregister(struct drm_device *dev)
return -EINVAL;
}
list_for_each_entry(subdrv, &exynos_drm_subdrv_list, list)
list_for_each_entry(subdrv, &exynos_drm_subdrv_list, list) {
exynos_drm_subdrv_remove(dev, subdrv);
exynos_drm_destroy_enc_conn(subdrv);
}
return 0;
}
......
......@@ -66,7 +66,6 @@ struct exynos_drm_crtc {
static void exynos_drm_crtc_dpms(struct drm_crtc *crtc, int mode)
{
struct drm_device *dev = crtc->dev;
struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
DRM_DEBUG_KMS("crtc[%d] mode[%d]\n", crtc->base.id, mode);
......@@ -76,12 +75,8 @@ static void exynos_drm_crtc_dpms(struct drm_crtc *crtc, int mode)
return;
}
mutex_lock(&dev->struct_mutex);
exynos_drm_fn_encoder(crtc, &mode, exynos_drm_encoder_crtc_dpms);
exynos_crtc->dpms = mode;
mutex_unlock(&dev->struct_mutex);
}
static void exynos_drm_crtc_prepare(struct drm_crtc *crtc)
......@@ -97,6 +92,7 @@ static void exynos_drm_crtc_commit(struct drm_crtc *crtc)
DRM_DEBUG_KMS("%s\n", __FILE__);
exynos_drm_crtc_dpms(crtc, DRM_MODE_DPMS_ON);
exynos_plane_commit(exynos_crtc->plane);
exynos_plane_dpms(exynos_crtc->plane, DRM_MODE_DPMS_ON);
}
......@@ -126,8 +122,6 @@ exynos_drm_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode,
DRM_DEBUG_KMS("%s\n", __FILE__);
exynos_drm_crtc_dpms(crtc, DRM_MODE_DPMS_ON);
/*
* copy the mode data adjusted by mode_fixup() into crtc->mode
* so that hardware can be seet to proper mode.
......@@ -161,6 +155,12 @@ static int exynos_drm_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
DRM_DEBUG_KMS("%s\n", __FILE__);
/* when framebuffer changing is requested, crtc's dpms should be on */
if (exynos_crtc->dpms > DRM_MODE_DPMS_ON) {
DRM_ERROR("failed framebuffer changing request.\n");
return -EPERM;
}
crtc_w = crtc->fb->width - x;
crtc_h = crtc->fb->height - y;
......@@ -213,6 +213,12 @@ static int exynos_drm_crtc_page_flip(struct drm_crtc *crtc,
DRM_DEBUG_KMS("%s\n", __FILE__);
/* when the page flip is requested, crtc's dpms should be on */
if (exynos_crtc->dpms > DRM_MODE_DPMS_ON) {
DRM_ERROR("failed page flip request.\n");
return -EINVAL;
}
mutex_lock(&dev->struct_mutex);
if (event) {
......
......@@ -36,6 +36,20 @@
#define MAX_FB_BUFFER 4
#define DEFAULT_ZPOS -1
#define _wait_for(COND, MS) ({ \
unsigned long timeout__ = jiffies + msecs_to_jiffies(MS); \
int ret__ = 0; \
while (!(COND)) { \
if (time_after(jiffies, timeout__)) { \
ret__ = -ETIMEDOUT; \
break; \
} \
} \
ret__; \
})
#define wait_for(COND, MS) _wait_for(COND, MS)
struct drm_device;
struct exynos_drm_overlay;
struct drm_connector;
......@@ -60,6 +74,8 @@ enum exynos_drm_output_type {
* @commit: apply hardware specific overlay data to registers.
* @enable: enable hardware specific overlay.
* @disable: disable hardware specific overlay.
* @wait_for_vblank: wait for vblank interrupt to make sure that
* hardware overlay is disabled.
*/
struct exynos_drm_overlay_ops {
void (*mode_set)(struct device *subdrv_dev,
......@@ -67,6 +83,7 @@ struct exynos_drm_overlay_ops {
void (*commit)(struct device *subdrv_dev, int zpos);
void (*enable)(struct device *subdrv_dev, int zpos);
void (*disable)(struct device *subdrv_dev, int zpos);
void (*wait_for_vblank)(struct device *subdrv_dev);
};
/*
......@@ -265,7 +282,7 @@ struct exynos_drm_subdrv {
struct exynos_drm_manager *manager;
int (*probe)(struct drm_device *drm_dev, struct device *dev);
void (*remove)(struct drm_device *dev);
void (*remove)(struct drm_device *drm_dev, struct device *dev);
int (*open)(struct drm_device *drm_dev, struct device *dev,
struct drm_file *file);
void (*close)(struct drm_device *drm_dev, struct device *dev,
......
......@@ -31,6 +31,7 @@
#include "exynos_drm_drv.h"
#include "exynos_drm_encoder.h"
#include "exynos_drm_connector.h"
#define to_exynos_encoder(x) container_of(x, struct exynos_drm_encoder,\
drm_encoder)
......@@ -44,26 +45,23 @@
* @dpms: store the encoder dpms value.
*/
struct exynos_drm_encoder {
struct drm_crtc *old_crtc;
struct drm_encoder drm_encoder;
struct exynos_drm_manager *manager;
int dpms;
};
static void exynos_drm_display_power(struct drm_encoder *encoder, int mode)
static void exynos_drm_connector_power(struct drm_encoder *encoder, int mode)
{
struct drm_device *dev = encoder->dev;
struct drm_connector *connector;
struct exynos_drm_manager *manager = exynos_drm_get_manager(encoder);
list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
if (connector->encoder == encoder) {
struct exynos_drm_display_ops *display_ops =
manager->display_ops;
if (exynos_drm_best_encoder(connector) == encoder) {
DRM_DEBUG_KMS("connector[%d] dpms[%d]\n",
connector->base.id, mode);
if (display_ops && display_ops->power_on)
display_ops->power_on(manager->dev, mode);
exynos_drm_display_power(connector, mode);
}
}
}
......@@ -88,13 +86,13 @@ static void exynos_drm_encoder_dpms(struct drm_encoder *encoder, int mode)
case DRM_MODE_DPMS_ON:
if (manager_ops && manager_ops->apply)
manager_ops->apply(manager->dev);
exynos_drm_display_power(encoder, mode);
exynos_drm_connector_power(encoder, mode);
exynos_encoder->dpms = mode;
break;
case DRM_MODE_DPMS_STANDBY:
case DRM_MODE_DPMS_SUSPEND:
case DRM_MODE_DPMS_OFF:
exynos_drm_display_power(encoder, mode);
exynos_drm_connector_power(encoder, mode);
exynos_encoder->dpms = mode;
break;
default:
......@@ -127,24 +125,74 @@ exynos_drm_encoder_mode_fixup(struct drm_encoder *encoder,
return true;
}
static void disable_plane_to_crtc(struct drm_device *dev,
struct drm_crtc *old_crtc,
struct drm_crtc *new_crtc)
{
struct drm_plane *plane;
/*
* if old_crtc isn't same as encoder->crtc then it means that
* user changed crtc id to another one so the plane to old_crtc
* should be disabled and plane->crtc should be set to new_crtc
* (encoder->crtc)
*/
list_for_each_entry(plane, &dev->mode_config.plane_list, head) {
if (plane->crtc == old_crtc) {
/*
* do not change below call order.
*
* plane->funcs->disable_plane call checks
* if encoder->crtc is same as plane->crtc and if same
* then overlay_ops->disable callback will be called
* to diasble current hw overlay so plane->crtc should
* have new_crtc because new_crtc was set to
* encoder->crtc in advance.
*/
plane->crtc = new_crtc;
plane->funcs->disable_plane(plane);
}
}
}
static void exynos_drm_encoder_mode_set(struct drm_encoder *encoder,
struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
{
struct drm_device *dev = encoder->dev;
struct drm_connector *connector;
struct exynos_drm_manager *manager = exynos_drm_get_manager(encoder);
struct exynos_drm_manager_ops *manager_ops = manager->ops;
struct exynos_drm_manager *manager;
struct exynos_drm_manager_ops *manager_ops;
DRM_DEBUG_KMS("%s\n", __FILE__);
exynos_drm_encoder_dpms(encoder, DRM_MODE_DPMS_ON);
list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
if (connector->encoder == encoder)
if (connector->encoder == encoder) {
struct exynos_drm_encoder *exynos_encoder;
exynos_encoder = to_exynos_encoder(encoder);
if (exynos_encoder->old_crtc != encoder->crtc &&
exynos_encoder->old_crtc) {
/*
* disable a plane to old crtc and change
* crtc of the plane to new one.
*/
disable_plane_to_crtc(dev,
exynos_encoder->old_crtc,
encoder->crtc);
}
manager = exynos_drm_get_manager(encoder);
manager_ops = manager->ops;
if (manager_ops && manager_ops->mode_set)
manager_ops->mode_set(manager->dev,
adjusted_mode);
exynos_encoder->old_crtc = encoder->crtc;
}
}
}
......@@ -166,12 +214,27 @@ static void exynos_drm_encoder_commit(struct drm_encoder *encoder)
manager_ops->commit(manager->dev);
}
static void exynos_drm_encoder_disable(struct drm_encoder *encoder)
{
struct drm_plane *plane;
struct drm_device *dev = encoder->dev;
exynos_drm_encoder_dpms(encoder, DRM_MODE_DPMS_OFF);
/* all planes connected to this encoder should be also disabled. */
list_for_each_entry(plane, &dev->mode_config.plane_list, head) {
if (plane->crtc == encoder->crtc)
plane->funcs->disable_plane(plane);
}
}
static struct drm_encoder_helper_funcs exynos_encoder_helper_funcs = {
.dpms = exynos_drm_encoder_dpms,
.mode_fixup = exynos_drm_encoder_mode_fixup,
.mode_set = exynos_drm_encoder_mode_set,
.prepare = exynos_drm_encoder_prepare,
.commit = exynos_drm_encoder_commit,
.disable = exynos_drm_encoder_disable,
};
static void exynos_drm_encoder_destroy(struct drm_encoder *encoder)
......@@ -337,6 +400,19 @@ void exynos_drm_encoder_crtc_dpms(struct drm_encoder *encoder, void *data)
if (manager_ops && manager_ops->dpms)
manager_ops->dpms(manager->dev, mode);
/*
* set current mode to new one so that data aren't updated into
* registers by drm_helper_connector_dpms two times.
*
* in case that drm_crtc_helper_set_mode() is called,
* overlay_ops->commit() and manager_ops->commit() callbacks
* can be called two times, first at drm_crtc_helper_set_mode()
* and second at drm_helper_connector_dpms().
* so with this setting, when drm_helper_connector_dpms() is called
* encoder->funcs->dpms() will be ignored.
*/
exynos_encoder->dpms = mode;
/*
* if this condition is ok then it means that the crtc is already
* detached from encoder and last function for detaching is properly
......@@ -422,4 +498,14 @@ void exynos_drm_encoder_plane_disable(struct drm_encoder *encoder, void *data)
if (overlay_ops && overlay_ops->disable)
overlay_ops->disable(manager->dev, zpos);
/*
* wait for vblank interrupt
* - this makes sure that hardware overlay is disabled to avoid
* for the dma accesses to memory after gem buffer was released
* because the setting for disabling the overlay will be updated
* at vsync.
*/
if (overlay_ops->wait_for_vblank)
overlay_ops->wait_for_vblank(manager->dev);
}
......@@ -41,10 +41,12 @@
* exynos specific framebuffer structure.
*
* @fb: drm framebuffer obejct.
* @buf_cnt: a buffer count to drm framebuffer.
* @exynos_gem_obj: array of exynos specific gem object containing a gem object.
*/
struct exynos_drm_fb {
struct drm_framebuffer fb;
unsigned int buf_cnt;
struct exynos_drm_gem_obj *exynos_gem_obj[MAX_FB_BUFFER];
};
......@@ -101,6 +103,25 @@ static struct drm_framebuffer_funcs exynos_drm_fb_funcs = {
.dirty = exynos_drm_fb_dirty,
};
void exynos_drm_fb_set_buf_cnt(struct drm_framebuffer *fb,
unsigned int cnt)
{
struct exynos_drm_fb *exynos_fb;
exynos_fb = to_exynos_fb(fb);
exynos_fb->buf_cnt = cnt;
}
unsigned int exynos_drm_fb_get_buf_cnt(struct drm_framebuffer *fb)
{
struct exynos_drm_fb *exynos_fb;
exynos_fb = to_exynos_fb(fb);
return exynos_fb->buf_cnt;
}
struct drm_framebuffer *
exynos_drm_framebuffer_init(struct drm_device *dev,
struct drm_mode_fb_cmd2 *mode_cmd,
......@@ -127,6 +148,43 @@ exynos_drm_framebuffer_init(struct drm_device *dev,
return &exynos_fb->fb;
}
static u32 exynos_drm_format_num_buffers(struct drm_mode_fb_cmd2 *mode_cmd)
{
unsigned int cnt = 0;
if (mode_cmd->pixel_format != DRM_FORMAT_NV12)
return drm_format_num_planes(mode_cmd->pixel_format);
while (cnt != MAX_FB_BUFFER) {
if (!mode_cmd->handles[cnt])
break;
cnt++;
}
/*
* check if NV12 or NV12M.
*
* NV12
* handles[0] = base1, offsets[0] = 0
* handles[1] = base1, offsets[1] = Y_size
*
* NV12M
* handles[0] = base1, offsets[0] = 0
* handles[1] = base2, offsets[1] = 0
*/
if (cnt == 2) {
/*
* in case of NV12 format, offsets[1] is not 0 and
* handles[0] is same as handles[1].
*/
if (mode_cmd->offsets[1] &&
mode_cmd->handles[0] == mode_cmd->handles[1])
cnt = 1;
}
return cnt;
}
static struct drm_framebuffer *
exynos_user_fb_create(struct drm_device *dev, struct drm_file *file_priv,
struct drm_mode_fb_cmd2 *mode_cmd)
......@@ -134,7 +192,6 @@ exynos_user_fb_create(struct drm_device *dev, struct drm_file *file_priv,
struct drm_gem_object *obj;
struct drm_framebuffer *fb;
struct exynos_drm_fb *exynos_fb;
int nr;
int i;
DRM_DEBUG_KMS("%s\n", __FILE__);
......@@ -152,9 +209,11 @@ exynos_user_fb_create(struct drm_device *dev, struct drm_file *file_priv,
}
exynos_fb = to_exynos_fb(fb);
nr = exynos_drm_format_num_buffers(fb->pixel_format);
exynos_fb->buf_cnt = exynos_drm_format_num_buffers(mode_cmd);
DRM_DEBUG_KMS("buf_cnt = %d\n", exynos_fb->buf_cnt);
for (i = 1; i < nr; i++) {
for (i = 1; i < exynos_fb->buf_cnt; i++) {
obj = drm_gem_object_lookup(dev, file_priv,
mode_cmd->handles[i]);
if (!obj) {
......
......@@ -28,19 +28,6 @@
#ifndef _EXYNOS_DRM_FB_H_
#define _EXYNOS_DRM_FB_H
static inline int exynos_drm_format_num_buffers(uint32_t format)
{
switch (format) {
case DRM_FORMAT_NV12:
case DRM_FORMAT_NV12MT:
return 2;
case DRM_FORMAT_YUV420:
return 3;
default:
return 1;
}
}
struct drm_framebuffer *
exynos_drm_framebuffer_init(struct drm_device *dev,
struct drm_mode_fb_cmd2 *mode_cmd,
......@@ -52,4 +39,11 @@ struct exynos_drm_gem_buf *exynos_drm_fb_buffer(struct drm_framebuffer *fb,
void exynos_drm_mode_config_init(struct drm_device *dev);
/* set a buffer count to drm framebuffer. */
void exynos_drm_fb_set_buf_cnt(struct drm_framebuffer *fb,
unsigned int cnt);
/* get a buffer count to drm framebuffer. */
unsigned int exynos_drm_fb_get_buf_cnt(struct drm_framebuffer *fb);
#endif
......@@ -79,6 +79,9 @@ static int exynos_drm_fbdev_update(struct drm_fb_helper *helper,
return -EFAULT;
}
/* buffer count to framebuffer always is 1 at booting time. */
exynos_drm_fb_set_buf_cnt(fb, 1);
offset = fbi->var.xoffset * (fb->bits_per_pixel >> 3);
offset += fbi->var.yoffset * fb->pitches[0];
......
......@@ -57,6 +57,18 @@
#define get_fimd_context(dev) platform_get_drvdata(to_platform_device(dev))
struct fimd_driver_data {
unsigned int timing_base;
};
struct fimd_driver_data exynos4_fimd_driver_data = {
.timing_base = 0x0,
};
struct fimd_driver_data exynos5_fimd_driver_data = {
.timing_base = 0x20000,
};
struct fimd_win_data {
unsigned int offset_x;
unsigned int offset_y;
......@@ -91,6 +103,13 @@ struct fimd_context {
struct exynos_drm_panel_info *panel;
};
static inline struct fimd_driver_data *drm_fimd_get_driver_data(
struct platform_device *pdev)
{
return (struct fimd_driver_data *)
platform_get_device_id(pdev)->driver_data;
}
static bool fimd_display_is_connected(struct device *dev)
{
DRM_DEBUG_KMS("%s\n", __FILE__);
......@@ -194,32 +213,35 @@ static void fimd_commit(struct device *dev)
struct fimd_context *ctx = get_fimd_context(dev);
struct exynos_drm_panel_info *panel = ctx->panel;
struct fb_videomode *timing = &panel->timing;
struct fimd_driver_data *driver_data;
struct platform_device *pdev = to_platform_device(dev);
u32 val;
driver_data = drm_fimd_get_driver_data(pdev);
if (ctx->suspended)
return;
DRM_DEBUG_KMS("%s\n", __FILE__);
/* setup polarity values from machine code. */
writel(ctx->vidcon1, ctx->regs + VIDCON1);
writel(ctx->vidcon1, ctx->regs + driver_data->timing_base + VIDCON1);
/* setup vertical timing values. */
val = VIDTCON0_VBPD(timing->upper_margin - 1) |
VIDTCON0_VFPD(timing->lower_margin - 1) |
VIDTCON0_VSPW(timing->vsync_len - 1);
writel(val, ctx->regs + VIDTCON0);
writel(val, ctx->regs + driver_data->timing_base + VIDTCON0);
/* setup horizontal timing values. */
val = VIDTCON1_HBPD(timing->left_margin - 1) |
VIDTCON1_HFPD(timing->right_margin - 1) |
VIDTCON1_HSPW(timing->hsync_len - 1);
writel(val, ctx->regs + VIDTCON1);
writel(val, ctx->regs + driver_data->timing_base + VIDTCON1);
/* setup horizontal and vertical display size. */
val = VIDTCON2_LINEVAL(timing->yres - 1) |
VIDTCON2_HOZVAL(timing->xres - 1);
writel(val, ctx->regs + VIDTCON2);
writel(val, ctx->regs + driver_data->timing_base + VIDTCON2);
/* setup clock source, clock divider, enable dma. */
val = ctx->vidcon0;
......@@ -570,10 +592,22 @@ static void fimd_win_disable(struct device *dev, int zpos)
win_data->enabled = false;
}
static void fimd_wait_for_vblank(struct device *dev)
{
struct fimd_context *ctx = get_fimd_context(dev);
int ret;
ret = wait_for((__raw_readl(ctx->regs + VIDCON1) &
VIDCON1_VSTATUS_VSYNC), 50);
if (ret < 0)
DRM_DEBUG_KMS("vblank wait timed out.\n");
}
static struct exynos_drm_overlay_ops fimd_overlay_ops = {
.mode_set = fimd_win_mode_set,
.commit = fimd_win_commit,
.disable = fimd_win_disable,
.wait_for_vblank = fimd_wait_for_vblank,
};
static struct exynos_drm_manager fimd_manager = {
......@@ -678,7 +712,7 @@ static int fimd_subdrv_probe(struct drm_device *drm_dev, struct device *dev)
return 0;
}
static void fimd_subdrv_remove(struct drm_device *drm_dev)
static void fimd_subdrv_remove(struct drm_device *drm_dev, struct device *dev)
{
DRM_DEBUG_KMS("%s\n", __FILE__);
......@@ -747,16 +781,10 @@ static void fimd_clear_win(struct fimd_context *ctx, int win)
writel(val, ctx->regs + SHADOWCON);
}
static int fimd_power_on(struct fimd_context *ctx, bool enable)
static int fimd_clock(struct fimd_context *ctx, bool enable)
{
struct exynos_drm_subdrv *subdrv = &ctx->subdrv;
struct device *dev = subdrv->dev;
DRM_DEBUG_KMS("%s\n", __FILE__);
if (enable != false && enable != true)
return -EINVAL;
if (enable) {
int ret;
......@@ -769,18 +797,31 @@ static int fimd_power_on(struct fimd_context *ctx, bool enable)
clk_disable(ctx->bus_clk);
return ret;
}
} else {
clk_disable(ctx->lcd_clk);
clk_disable(ctx->bus_clk);
}
return 0;
}
static int fimd_activate(struct fimd_context *ctx, bool enable)
{
if (enable) {
int ret;
struct device *dev = ctx->subdrv.dev;
ret = fimd_clock(ctx, true);
if (ret < 0)
return ret;
ctx->suspended = false;
/* if vblank was enabled status, enable it again. */
if (test_and_clear_bit(0, &ctx->irq_flags))
fimd_enable_vblank(dev);
fimd_apply(dev);
} else {
clk_disable(ctx->lcd_clk);
clk_disable(ctx->bus_clk);
fimd_clock(ctx, false);
ctx->suspended = true;
}
......@@ -930,15 +971,15 @@ static int fimd_suspend(struct device *dev)
{
struct fimd_context *ctx = get_fimd_context(dev);
if (pm_runtime_suspended(dev))
return 0;
/*
* do not use pm_runtime_suspend(). if pm_runtime_suspend() is
* called here, an error would be returned by that interface
* because the usage_count of pm runtime is more than 1.
*/
return fimd_power_on(ctx, false);
if (!pm_runtime_suspended(dev))
return fimd_activate(ctx, false);
return 0;
}
static int fimd_resume(struct device *dev)
......@@ -950,8 +991,21 @@ static int fimd_resume(struct device *dev)
* of pm runtime would still be 1 so in this case, fimd driver
* should be on directly not drawing on pm runtime interface.
*/
if (!pm_runtime_suspended(dev))
return fimd_power_on(ctx, true);
if (pm_runtime_suspended(dev)) {
int ret;
ret = fimd_activate(ctx, true);
if (ret < 0)
return ret;
/*
* in case of dpms on(standby), fimd_apply function will
* be called by encoder's dpms callback to update fimd's
* registers but in case of sleep wakeup, it's not.
* so fimd_apply function should be called at here.
*/
fimd_apply(dev);
}
return 0;
}
......@@ -964,7 +1018,7 @@ static int fimd_runtime_suspend(struct device *dev)
DRM_DEBUG_KMS("%s\n", __FILE__);
return fimd_power_on(ctx, false);
return fimd_activate(ctx, false);
}
static int fimd_runtime_resume(struct device *dev)
......@@ -973,10 +1027,22 @@ static int fimd_runtime_resume(struct device *dev)
DRM_DEBUG_KMS("%s\n", __FILE__);
return fimd_power_on(ctx, true);
return fimd_activate(ctx, true);
}
#endif
static struct platform_device_id fimd_driver_ids[] = {
{
.name = "exynos4-fb",
.driver_data = (unsigned long)&exynos4_fimd_driver_data,
}, {
.name = "exynos5-fb",
.driver_data = (unsigned long)&exynos5_fimd_driver_data,
},
{},
};
MODULE_DEVICE_TABLE(platform, fimd_driver_ids);
static const struct dev_pm_ops fimd_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(fimd_suspend, fimd_resume)
SET_RUNTIME_PM_OPS(fimd_runtime_suspend, fimd_runtime_resume, NULL)
......@@ -985,6 +1051,7 @@ static const struct dev_pm_ops fimd_pm_ops = {
struct platform_driver fimd_driver = {
.probe = fimd_probe,
.remove = __devexit_p(fimd_remove),
.id_table = fimd_driver_ids,
.driver = {
.name = "exynos4-fb",
.owner = THIS_MODULE,
......
......@@ -122,6 +122,7 @@ struct g2d_runqueue_node {
struct list_head list;
struct list_head run_cmdlist;
struct list_head event_list;
pid_t pid;
struct completion complete;
int async;
};
......@@ -164,8 +165,7 @@ static int g2d_init_cmdlist(struct g2d_data *g2d)
return -ENOMEM;
}
node = kcalloc(G2D_CMDLIST_NUM, G2D_CMDLIST_NUM * sizeof(*node),
GFP_KERNEL);
node = kcalloc(G2D_CMDLIST_NUM, sizeof(*node), GFP_KERNEL);
if (!node) {
dev_err(dev, "failed to allocate memory\n");
ret = -ENOMEM;
......@@ -679,6 +679,7 @@ int exynos_g2d_exec_ioctl(struct drm_device *drm_dev, void *data,
}
mutex_lock(&g2d->runqueue_mutex);
runqueue_node->pid = current->pid;
list_add_tail(&runqueue_node->list, &g2d->runqueue);
if (!g2d->runqueue_node)
g2d_exec_runqueue(g2d);
......
......@@ -29,6 +29,11 @@
#define get_ctx_from_subdrv(subdrv) container_of(subdrv,\
struct drm_hdmi_context, subdrv);
/* Common hdmi subdrv needs to access the hdmi and mixer though context.
* These should be initialied by the repective drivers */
static struct exynos_drm_hdmi_context *hdmi_ctx;
static struct exynos_drm_hdmi_context *mixer_ctx;
/* these callback points shoud be set by specific drivers. */
static struct exynos_hdmi_ops *hdmi_ops;
static struct exynos_mixer_ops *mixer_ops;
......@@ -41,6 +46,18 @@ struct drm_hdmi_context {
bool enabled[MIXER_WIN_NR];
};
void exynos_hdmi_drv_attach(struct exynos_drm_hdmi_context *ctx)
{
if (ctx)
hdmi_ctx = ctx;
}
void exynos_mixer_drv_attach(struct exynos_drm_hdmi_context *ctx)
{
if (ctx)
mixer_ctx = ctx;
}
void exynos_hdmi_ops_register(struct exynos_hdmi_ops *ops)
{
DRM_DEBUG_KMS("%s\n", __FILE__);
......@@ -274,10 +291,21 @@ static void drm_mixer_disable(struct device *subdrv_dev, int zpos)
ctx->enabled[win] = false;
}
static void drm_mixer_wait_for_vblank(struct device *subdrv_dev)
{
struct drm_hdmi_context *ctx = to_context(subdrv_dev);
DRM_DEBUG_KMS("%s\n", __FILE__);
if (mixer_ops && mixer_ops->wait_for_vblank)
mixer_ops->wait_for_vblank(ctx->mixer_ctx->ctx);
}
static struct exynos_drm_overlay_ops drm_hdmi_overlay_ops = {
.mode_set = drm_mixer_mode_set,
.commit = drm_mixer_commit,
.disable = drm_mixer_disable,
.wait_for_vblank = drm_mixer_wait_for_vblank,
};
static struct exynos_drm_manager hdmi_manager = {
......@@ -292,46 +320,30 @@ static int hdmi_subdrv_probe(struct drm_device *drm_dev,
{
struct exynos_drm_subdrv *subdrv = to_subdrv(dev);
struct drm_hdmi_context *ctx;
struct platform_device *pdev = to_platform_device(dev);
struct exynos_drm_common_hdmi_pd *pd;
DRM_DEBUG_KMS("%s\n", __FILE__);
pd = pdev->dev.platform_data;
if (!pd) {
DRM_DEBUG_KMS("platform data is null.\n");
return -EFAULT;
}
if (!pd->hdmi_dev) {
DRM_DEBUG_KMS("hdmi device is null.\n");
if (!hdmi_ctx) {
DRM_ERROR("hdmi context not initialized.\n");
return -EFAULT;
}
if (!pd->mixer_dev) {
DRM_DEBUG_KMS("mixer device is null.\n");
if (!mixer_ctx) {
DRM_ERROR("mixer context not initialized.\n");
return -EFAULT;
}
ctx = get_ctx_from_subdrv(subdrv);
ctx->hdmi_ctx = (struct exynos_drm_hdmi_context *)
to_context(pd->hdmi_dev);
if (!ctx->hdmi_ctx) {
DRM_DEBUG_KMS("hdmi context is null.\n");
if (!ctx) {
DRM_ERROR("no drm hdmi context.\n");
return -EFAULT;
}
ctx->hdmi_ctx->drm_dev = drm_dev;
ctx->mixer_ctx = (struct exynos_drm_hdmi_context *)
to_context(pd->mixer_dev);
if (!ctx->mixer_ctx) {
DRM_DEBUG_KMS("mixer context is null.\n");
return -EFAULT;
}
ctx->hdmi_ctx = hdmi_ctx;
ctx->mixer_ctx = mixer_ctx;
ctx->hdmi_ctx->drm_dev = drm_dev;
ctx->mixer_ctx->drm_dev = drm_dev;
return 0;
......
......@@ -67,11 +67,14 @@ struct exynos_mixer_ops {
void (*dpms)(void *ctx, int mode);
/* overlay */
void (*wait_for_vblank)(void *ctx);
void (*win_mode_set)(void *ctx, struct exynos_drm_overlay *overlay);
void (*win_commit)(void *ctx, int zpos);
void (*win_disable)(void *ctx, int zpos);
};
void exynos_hdmi_drv_attach(struct exynos_drm_hdmi_context *ctx);
void exynos_mixer_drv_attach(struct exynos_drm_hdmi_context *ctx);
void exynos_hdmi_ops_register(struct exynos_hdmi_ops *ops);
void exynos_mixer_ops_register(struct exynos_mixer_ops *ops);
#endif
......@@ -32,6 +32,42 @@ static const uint32_t formats[] = {
DRM_FORMAT_NV12MT,
};
/*
* This function is to get X or Y size shown via screen. This needs length and
* start position of CRTC.
*
* <--- length --->
* CRTC ----------------
* ^ start ^ end
*
* There are six cases from a to b.
*
* <----- SCREEN ----->
* 0 last
* ----------|------------------|----------
* CRTCs
* a -------
* b -------
* c --------------------------
* d --------
* e -------
* f -------
*/
static int exynos_plane_get_size(int start, unsigned length, unsigned last)
{
int end = start + length;
int size = 0;
if (start <= 0) {
if (end > 0)
size = min_t(unsigned, end, last);
} else if (start <= last) {
size = min_t(unsigned, last - start, length);
}
return size;
}
int exynos_plane_mode_set(struct drm_plane *plane, struct drm_crtc *crtc,
struct drm_framebuffer *fb, int crtc_x, int crtc_y,
unsigned int crtc_w, unsigned int crtc_h,
......@@ -47,7 +83,7 @@ int exynos_plane_mode_set(struct drm_plane *plane, struct drm_crtc *crtc,
DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
nr = exynos_drm_format_num_buffers(fb->pixel_format);
nr = exynos_drm_fb_get_buf_cnt(fb);
for (i = 0; i < nr; i++) {
struct exynos_drm_gem_buf *buffer = exynos_drm_fb_buffer(fb, i);
......@@ -64,8 +100,24 @@ int exynos_plane_mode_set(struct drm_plane *plane, struct drm_crtc *crtc,
(unsigned long)overlay->dma_addr[i]);
}
actual_w = min((unsigned)(crtc->mode.hdisplay - crtc_x), crtc_w);
actual_h = min((unsigned)(crtc->mode.vdisplay - crtc_y), crtc_h);
actual_w = exynos_plane_get_size(crtc_x, crtc_w, crtc->mode.hdisplay);
actual_h = exynos_plane_get_size(crtc_y, crtc_h, crtc->mode.vdisplay);
if (crtc_x < 0) {
if (actual_w)
src_x -= crtc_x;
else
src_x += crtc_w;
crtc_x = 0;
}
if (crtc_y < 0) {
if (actual_h)
src_y -= crtc_y;
else
src_y += crtc_h;
crtc_y = 0;
}
/* set drm framebuffer data. */
overlay->fb_x = src_x;
......
......@@ -56,6 +56,7 @@ struct vidi_context {
unsigned int connected;
bool vblank_on;
bool suspended;
bool direct_vblank;
struct work_struct work;
struct mutex lock;
};
......@@ -224,6 +225,15 @@ static int vidi_enable_vblank(struct device *dev)
if (!test_and_set_bit(0, &ctx->irq_flags))
ctx->vblank_on = true;
ctx->direct_vblank = true;
/*
* in case of page flip request, vidi_finish_pageflip function
* will not be called because direct_vblank is true and then
* that function will be called by overlay_ops->commit callback
*/
schedule_work(&ctx->work);
return 0;
}
......@@ -425,7 +435,17 @@ static void vidi_fake_vblank_handler(struct work_struct *work)
/* refresh rate is about 50Hz. */
usleep_range(16000, 20000);
mutex_lock(&ctx->lock);
if (ctx->direct_vblank) {
drm_handle_vblank(subdrv->drm_dev, manager->pipe);
ctx->direct_vblank = false;
mutex_unlock(&ctx->lock);
return;
}
mutex_unlock(&ctx->lock);
vidi_finish_pageflip(subdrv->drm_dev, manager->pipe);
}
......@@ -453,7 +473,7 @@ static int vidi_subdrv_probe(struct drm_device *drm_dev, struct device *dev)
return 0;
}
static void vidi_subdrv_remove(struct drm_device *drm_dev)
static void vidi_subdrv_remove(struct drm_device *drm_dev, struct device *dev)
{
DRM_DEBUG_KMS("%s\n", __FILE__);
......
This diff is collapsed.
......@@ -42,13 +42,23 @@ static int hdmiphy_remove(struct i2c_client *client)
static const struct i2c_device_id hdmiphy_id[] = {
{ "s5p_hdmiphy", 0 },
{ "exynos5-hdmiphy", 0 },
{ },
};
static struct of_device_id hdmiphy_match_types[] = {
{
.compatible = "samsung,exynos5-hdmiphy",
}, {
/* end node */
}
};
struct i2c_driver hdmiphy_driver = {
.driver = {
.name = "s5p-hdmiphy",
.name = "exynos-hdmiphy",
.owner = THIS_MODULE,
.of_match_table = hdmiphy_match_types,
},
.id_table = hdmiphy_id,
.probe = hdmiphy_probe,
......
This diff is collapsed.
......@@ -69,6 +69,7 @@
(((val) << (low_bit)) & MXR_MASK(high_bit, low_bit))
/* bits for MXR_STATUS */
#define MXR_STATUS_SOFT_RESET (1 << 8)
#define MXR_STATUS_16_BURST (1 << 7)
#define MXR_STATUS_BURST_MASK (1 << 7)
#define MXR_STATUS_BIG_ENDIAN (1 << 3)
......@@ -77,6 +78,8 @@
#define MXR_STATUS_REG_RUN (1 << 0)
/* bits for MXR_CFG */
#define MXR_CFG_LAYER_UPDATE (1 << 31)
#define MXR_CFG_LAYER_UPDATE_COUNT_MASK (3 << 29)
#define MXR_CFG_RGB601_0_255 (0 << 9)
#define MXR_CFG_RGB601_16_235 (1 << 9)
#define MXR_CFG_RGB709_0_255 (2 << 9)
......
......@@ -30,7 +30,6 @@
#include <uapi/drm/exynos_drm.h>
/**
* A structure for lcd panel information.
*
......
......@@ -20,6 +20,7 @@ struct i2c_board_info;
* @hdmiphy_info: template for HDMIPHY I2C device
* @mhl_bus: controller id for MHL control bus
* @mhl_info: template for MHL I2C device
* @hpd_gpio: GPIO for Hot-Plug-Detect pin
*
* NULL pointer for *_info fields indicates that
* the corresponding chip is not present
......@@ -29,6 +30,7 @@ struct s5p_hdmi_platform_data {
struct i2c_board_info *hdmiphy_info;
int mhl_bus;
struct i2c_board_info *mhl_info;
int hpd_gpio;
};
#endif /* S5P_HDMI_H */
......
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