Commit 2f5b4ef1 authored by Dave Airlie's avatar Dave Airlie

Merge tag 'drm/tegra/for-3.20-rc1' of git://anongit.freedesktop.org/tegra/linux into drm-next

drm/tegra: Changes for v3.20-rc1

The biggest part of these changes is the conversion to atomic mode-
setting. A lot of cleanup and demidlayering was required before the
conversion, with the result being a whole lot of changes.

Besides the atomic mode-setting support, the host1x bus now has the
proper infrastructure to support suspend/resume for child devices.

Finally, a couple of smaller cleanup patches round things off.

* tag 'drm/tegra/for-3.20-rc1' of git://anongit.freedesktop.org/tegra/linux: (54 commits)
  drm/tegra: Use correct relocation target offsets
  drm/tegra: Add minimal power management
  drm/tegra: dc: Unify enabling the display controller
  drm/tegra: Track tiling and format in plane state
  drm/tegra: Track active planes in CRTC state
  drm/tegra: Remove unused ->mode_fixup() callbacks
  drm/tegra: Atomic conversion, phase 3, step 3
  drm/tegra: Atomic conversion, phase 3, step 2
  drm/tegra: dc: Use atomic clock state in modeset
  drm/tegra: sor: Implement ->atomic_check()
  drm/tegra: hdmi: Implement ->atomic_check()
  drm/tegra: dsi: Implement ->atomic_check()
  drm/tegra: rgb: Implement ->atomic_check()
  drm/tegra: dc: Store clock setup in atomic state
  drm/tegra: Atomic conversion, phase 3, step 1
  drm/tegra: Atomic conversion, phase 2
  drm/tegra: Atomic conversion, phase 1
  drm/tegra: dc: Do not needlessly deassert reset
  drm/tegra: Output cleanup functions cannot fail
  drm/tegra: Remove remnants of the output midlayer
  ...
parents 1da30627 31f40f86
obj-y += drm/ vga/ # drm/tegra depends on host1x, so if both drivers are built-in care must be
# taken to initialize them in the correct order. Link order is the only way
# to ensure this currently.
obj-$(CONFIG_TEGRA_HOST1X) += host1x/ obj-$(CONFIG_TEGRA_HOST1X) += host1x/
obj-y += drm/ vga/
obj-$(CONFIG_IMX_IPUV3_CORE) += ipu-v3/ obj-$(CONFIG_IMX_IPUV3_CORE) += ipu-v3/
...@@ -297,7 +297,15 @@ mode_fixup(struct drm_atomic_state *state) ...@@ -297,7 +297,15 @@ mode_fixup(struct drm_atomic_state *state)
} }
} }
if (funcs->atomic_check) {
ret = funcs->atomic_check(encoder, crtc_state,
conn_state);
if (ret) {
DRM_DEBUG_KMS("[ENCODER:%d:%s] check failed\n",
encoder->base.id, encoder->name);
return ret;
}
} else {
ret = funcs->mode_fixup(encoder, &crtc_state->mode, ret = funcs->mode_fixup(encoder, &crtc_state->mode,
&crtc_state->adjusted_mode); &crtc_state->adjusted_mode);
if (!ret) { if (!ret) {
...@@ -306,6 +314,7 @@ mode_fixup(struct drm_atomic_state *state) ...@@ -306,6 +314,7 @@ mode_fixup(struct drm_atomic_state *state)
return -EINVAL; return -EINVAL;
} }
} }
}
for (i = 0; i < ncrtcs; i++) { for (i = 0; i < ncrtcs; i++) {
struct drm_crtc_helper_funcs *funcs; struct drm_crtc_helper_funcs *funcs;
...@@ -1108,11 +1117,18 @@ void drm_atomic_helper_commit_planes(struct drm_device *dev, ...@@ -1108,11 +1117,18 @@ void drm_atomic_helper_commit_planes(struct drm_device *dev,
funcs = plane->helper_private; funcs = plane->helper_private;
if (!funcs || !funcs->atomic_update) if (!funcs)
continue; continue;
old_plane_state = old_state->plane_states[i]; old_plane_state = old_state->plane_states[i];
/*
* Special-case disabling the plane if drivers support it.
*/
if (drm_atomic_plane_disabling(plane, old_plane_state) &&
funcs->atomic_disable)
funcs->atomic_disable(plane, old_plane_state);
else
funcs->atomic_update(plane, old_plane_state); funcs->atomic_update(plane, old_plane_state);
} }
......
...@@ -449,6 +449,14 @@ int drm_plane_helper_commit(struct drm_plane *plane, ...@@ -449,6 +449,14 @@ int drm_plane_helper_commit(struct drm_plane *plane,
crtc_funcs[i]->atomic_begin(crtc[i]); crtc_funcs[i]->atomic_begin(crtc[i]);
} }
/*
* Drivers may optionally implement the ->atomic_disable callback, so
* special-case that here.
*/
if (drm_atomic_plane_disabling(plane, plane_state) &&
plane_funcs->atomic_disable)
plane_funcs->atomic_disable(plane, plane_state);
else
plane_funcs->atomic_update(plane, plane_state); plane_funcs->atomic_update(plane, plane_state);
for (i = 0; i < 2; i++) { for (i = 0; i < 2; i++) {
......
This diff is collapsed.
...@@ -10,6 +10,9 @@ ...@@ -10,6 +10,9 @@
#include <linux/host1x.h> #include <linux/host1x.h>
#include <linux/iommu.h> #include <linux/iommu.h>
#include <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h>
#include "drm.h" #include "drm.h"
#include "gem.h" #include "gem.h"
...@@ -24,6 +27,92 @@ struct tegra_drm_file { ...@@ -24,6 +27,92 @@ struct tegra_drm_file {
struct list_head contexts; struct list_head contexts;
}; };
static void tegra_atomic_schedule(struct tegra_drm *tegra,
struct drm_atomic_state *state)
{
tegra->commit.state = state;
schedule_work(&tegra->commit.work);
}
static void tegra_atomic_complete(struct tegra_drm *tegra,
struct drm_atomic_state *state)
{
struct drm_device *drm = tegra->drm;
/*
* Everything below can be run asynchronously without the need to grab
* any modeset locks at all under one condition: It must be guaranteed
* that the asynchronous work has either been cancelled (if the driver
* supports it, which at least requires that the framebuffers get
* cleaned up with drm_atomic_helper_cleanup_planes()) or completed
* before the new state gets committed on the software side with
* drm_atomic_helper_swap_state().
*
* This scheme allows new atomic state updates to be prepared and
* checked in parallel to the asynchronous completion of the previous
* update. Which is important since compositors need to figure out the
* composition of the next frame right after having submitted the
* current layout.
*/
drm_atomic_helper_commit_pre_planes(drm, state);
drm_atomic_helper_commit_planes(drm, state);
drm_atomic_helper_commit_post_planes(drm, state);
drm_atomic_helper_wait_for_vblanks(drm, state);
drm_atomic_helper_cleanup_planes(drm, state);
drm_atomic_state_free(state);
}
static void tegra_atomic_work(struct work_struct *work)
{
struct tegra_drm *tegra = container_of(work, struct tegra_drm,
commit.work);
tegra_atomic_complete(tegra, tegra->commit.state);
}
static int tegra_atomic_commit(struct drm_device *drm,
struct drm_atomic_state *state, bool async)
{
struct tegra_drm *tegra = drm->dev_private;
int err;
err = drm_atomic_helper_prepare_planes(drm, state);
if (err)
return err;
/* serialize outstanding asynchronous commits */
mutex_lock(&tegra->commit.lock);
flush_work(&tegra->commit.work);
/*
* This is the point of no return - everything below never fails except
* when the hw goes bonghits. Which means we can commit the new state on
* the software side now.
*/
drm_atomic_helper_swap_state(drm, state);
if (async)
tegra_atomic_schedule(tegra, state);
else
tegra_atomic_complete(tegra, state);
mutex_unlock(&tegra->commit.lock);
return 0;
}
static const struct drm_mode_config_funcs tegra_drm_mode_funcs = {
.fb_create = tegra_fb_create,
#ifdef CONFIG_DRM_TEGRA_FBDEV
.output_poll_changed = tegra_fb_output_poll_changed,
#endif
.atomic_check = drm_atomic_helper_check,
.atomic_commit = tegra_atomic_commit,
};
static int tegra_drm_load(struct drm_device *drm, unsigned long flags) static int tegra_drm_load(struct drm_device *drm, unsigned long flags)
{ {
struct host1x_device *device = to_host1x_device(drm->dev); struct host1x_device *device = to_host1x_device(drm->dev);
...@@ -36,8 +125,8 @@ static int tegra_drm_load(struct drm_device *drm, unsigned long flags) ...@@ -36,8 +125,8 @@ static int tegra_drm_load(struct drm_device *drm, unsigned long flags)
if (iommu_present(&platform_bus_type)) { if (iommu_present(&platform_bus_type)) {
tegra->domain = iommu_domain_alloc(&platform_bus_type); tegra->domain = iommu_domain_alloc(&platform_bus_type);
if (IS_ERR(tegra->domain)) { if (!tegra->domain) {
err = PTR_ERR(tegra->domain); err = -ENOMEM;
goto free; goto free;
} }
...@@ -47,11 +136,23 @@ static int tegra_drm_load(struct drm_device *drm, unsigned long flags) ...@@ -47,11 +136,23 @@ static int tegra_drm_load(struct drm_device *drm, unsigned long flags)
mutex_init(&tegra->clients_lock); mutex_init(&tegra->clients_lock);
INIT_LIST_HEAD(&tegra->clients); INIT_LIST_HEAD(&tegra->clients);
mutex_init(&tegra->commit.lock);
INIT_WORK(&tegra->commit.work, tegra_atomic_work);
drm->dev_private = tegra; drm->dev_private = tegra;
tegra->drm = drm; tegra->drm = drm;
drm_mode_config_init(drm); drm_mode_config_init(drm);
drm->mode_config.min_width = 0;
drm->mode_config.min_height = 0;
drm->mode_config.max_width = 4096;
drm->mode_config.max_height = 4096;
drm->mode_config.funcs = &tegra_drm_mode_funcs;
err = tegra_drm_fb_prepare(drm); err = tegra_drm_fb_prepare(drm);
if (err < 0) if (err < 0)
goto config; goto config;
...@@ -62,6 +163,8 @@ static int tegra_drm_load(struct drm_device *drm, unsigned long flags) ...@@ -62,6 +163,8 @@ static int tegra_drm_load(struct drm_device *drm, unsigned long flags)
if (err < 0) if (err < 0)
goto fbdev; goto fbdev;
drm_mode_config_reset(drm);
/* /*
* We don't use the drm_irq_install() helpers provided by the DRM * We don't use the drm_irq_install() helpers provided by the DRM
* core, so we need to set this manually in order to allow the * core, so we need to set this manually in order to allow the
...@@ -106,8 +209,8 @@ static int tegra_drm_unload(struct drm_device *drm) ...@@ -106,8 +209,8 @@ static int tegra_drm_unload(struct drm_device *drm)
drm_kms_helper_poll_fini(drm); drm_kms_helper_poll_fini(drm);
tegra_drm_fb_exit(drm); tegra_drm_fb_exit(drm);
drm_vblank_cleanup(drm);
drm_mode_config_cleanup(drm); drm_mode_config_cleanup(drm);
drm_vblank_cleanup(drm);
err = host1x_device_exit(device); err = host1x_device_exit(device);
if (err < 0) if (err < 0)
...@@ -190,7 +293,7 @@ static int host1x_reloc_copy_from_user(struct host1x_reloc *dest, ...@@ -190,7 +293,7 @@ static int host1x_reloc_copy_from_user(struct host1x_reloc *dest,
if (err < 0) if (err < 0)
return err; return err;
err = get_user(dest->target.offset, &src->cmdbuf.offset); err = get_user(dest->target.offset, &src->target.offset);
if (err < 0) if (err < 0)
return err; return err;
...@@ -893,6 +996,30 @@ static int host1x_drm_remove(struct host1x_device *dev) ...@@ -893,6 +996,30 @@ static int host1x_drm_remove(struct host1x_device *dev)
return 0; return 0;
} }
#ifdef CONFIG_PM_SLEEP
static int host1x_drm_suspend(struct device *dev)
{
struct drm_device *drm = dev_get_drvdata(dev);
drm_kms_helper_poll_disable(drm);
return 0;
}
static int host1x_drm_resume(struct device *dev)
{
struct drm_device *drm = dev_get_drvdata(dev);
drm_kms_helper_poll_enable(drm);
return 0;
}
#endif
static const struct dev_pm_ops host1x_drm_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(host1x_drm_suspend, host1x_drm_resume)
};
static const struct of_device_id host1x_drm_subdevs[] = { static const struct of_device_id host1x_drm_subdevs[] = {
{ .compatible = "nvidia,tegra20-dc", }, { .compatible = "nvidia,tegra20-dc", },
{ .compatible = "nvidia,tegra20-hdmi", }, { .compatible = "nvidia,tegra20-hdmi", },
...@@ -912,7 +1039,10 @@ static const struct of_device_id host1x_drm_subdevs[] = { ...@@ -912,7 +1039,10 @@ static const struct of_device_id host1x_drm_subdevs[] = {
}; };
static struct host1x_driver host1x_drm_driver = { static struct host1x_driver host1x_drm_driver = {
.driver = {
.name = "drm", .name = "drm",
.pm = &host1x_drm_pm_ops,
},
.probe = host1x_drm_probe, .probe = host1x_drm_probe,
.remove = host1x_drm_remove, .remove = host1x_drm_remove,
.subdevs = host1x_drm_subdevs, .subdevs = host1x_drm_subdevs,
......
...@@ -50,6 +50,12 @@ struct tegra_drm { ...@@ -50,6 +50,12 @@ struct tegra_drm {
#endif #endif
unsigned int pitch_align; unsigned int pitch_align;
struct {
struct drm_atomic_state *state;
struct work_struct work;
struct mutex lock;
} commit;
}; };
struct tegra_drm_client; struct tegra_drm_client;
...@@ -164,45 +170,31 @@ struct tegra_dc_window { ...@@ -164,45 +170,31 @@ struct tegra_dc_window {
unsigned int h; unsigned int h;
} dst; } dst;
unsigned int bits_per_pixel; unsigned int bits_per_pixel;
unsigned int format;
unsigned int swap;
unsigned int stride[2]; unsigned int stride[2];
unsigned long base[3]; unsigned long base[3];
bool bottom_up; bool bottom_up;
struct tegra_bo_tiling tiling; struct tegra_bo_tiling tiling;
u32 format;
u32 swap;
}; };
/* from dc.c */ /* from dc.c */
void tegra_dc_enable_vblank(struct tegra_dc *dc); void tegra_dc_enable_vblank(struct tegra_dc *dc);
void tegra_dc_disable_vblank(struct tegra_dc *dc); void tegra_dc_disable_vblank(struct tegra_dc *dc);
void tegra_dc_cancel_page_flip(struct drm_crtc *crtc, struct drm_file *file); void tegra_dc_cancel_page_flip(struct drm_crtc *crtc, struct drm_file *file);
void tegra_dc_commit(struct tegra_dc *dc);
struct tegra_output_ops { int tegra_dc_setup_clock(struct tegra_dc *dc, struct clk *parent,
int (*enable)(struct tegra_output *output); unsigned long pclk, unsigned int div);
int (*disable)(struct tegra_output *output); int tegra_dc_state_setup_clock(struct tegra_dc *dc,
int (*setup_clock)(struct tegra_output *output, struct clk *clk, struct drm_crtc_state *crtc_state,
unsigned long pclk, unsigned int *div); struct clk *clk, unsigned long pclk,
int (*check_mode)(struct tegra_output *output, unsigned int div);
struct drm_display_mode *mode,
enum drm_mode_status *status);
enum drm_connector_status (*detect)(struct tegra_output *output);
};
enum tegra_output_type {
TEGRA_OUTPUT_RGB,
TEGRA_OUTPUT_HDMI,
TEGRA_OUTPUT_DSI,
TEGRA_OUTPUT_EDP,
};
struct tegra_output { struct tegra_output {
struct device_node *of_node; struct device_node *of_node;
struct device *dev; struct device *dev;
const struct tegra_output_ops *ops;
enum tegra_output_type type;
struct drm_panel *panel; struct drm_panel *panel;
struct i2c_adapter *ddc; struct i2c_adapter *ddc;
const struct edid *edid; const struct edid *edid;
...@@ -223,42 +215,6 @@ static inline struct tegra_output *connector_to_output(struct drm_connector *c) ...@@ -223,42 +215,6 @@ static inline struct tegra_output *connector_to_output(struct drm_connector *c)
return container_of(c, struct tegra_output, connector); return container_of(c, struct tegra_output, connector);
} }
static inline int tegra_output_enable(struct tegra_output *output)
{
if (output && output->ops && output->ops->enable)
return output->ops->enable(output);
return output ? -ENOSYS : -EINVAL;
}
static inline int tegra_output_disable(struct tegra_output *output)
{
if (output && output->ops && output->ops->disable)
return output->ops->disable(output);
return output ? -ENOSYS : -EINVAL;
}
static inline int tegra_output_setup_clock(struct tegra_output *output,
struct clk *clk, unsigned long pclk,
unsigned int *div)
{
if (output && output->ops && output->ops->setup_clock)
return output->ops->setup_clock(output, clk, pclk, div);
return output ? -ENOSYS : -EINVAL;
}
static inline int tegra_output_check_mode(struct tegra_output *output,
struct drm_display_mode *mode,
enum drm_mode_status *status)
{
if (output && output->ops && output->ops->check_mode)
return output->ops->check_mode(output, mode, status);
return output ? -ENOSYS : -EINVAL;
}
/* from rgb.c */ /* from rgb.c */
int tegra_dc_rgb_probe(struct tegra_dc *dc); int tegra_dc_rgb_probe(struct tegra_dc *dc);
int tegra_dc_rgb_remove(struct tegra_dc *dc); int tegra_dc_rgb_remove(struct tegra_dc *dc);
...@@ -267,9 +223,18 @@ int tegra_dc_rgb_exit(struct tegra_dc *dc); ...@@ -267,9 +223,18 @@ int tegra_dc_rgb_exit(struct tegra_dc *dc);
/* from output.c */ /* from output.c */
int tegra_output_probe(struct tegra_output *output); int tegra_output_probe(struct tegra_output *output);
int tegra_output_remove(struct tegra_output *output); void tegra_output_remove(struct tegra_output *output);
int tegra_output_init(struct drm_device *drm, struct tegra_output *output); int tegra_output_init(struct drm_device *drm, struct tegra_output *output);
int tegra_output_exit(struct tegra_output *output); void tegra_output_exit(struct tegra_output *output);
int tegra_output_connector_get_modes(struct drm_connector *connector);
struct drm_encoder *
tegra_output_connector_best_encoder(struct drm_connector *connector);
enum drm_connector_status
tegra_output_connector_detect(struct drm_connector *connector, bool force);
void tegra_output_connector_destroy(struct drm_connector *connector);
void tegra_output_encoder_destroy(struct drm_encoder *encoder);
/* from dpaux.c */ /* from dpaux.c */
struct tegra_dpaux; struct tegra_dpaux;
...@@ -291,12 +256,16 @@ struct tegra_bo *tegra_fb_get_plane(struct drm_framebuffer *framebuffer, ...@@ -291,12 +256,16 @@ struct tegra_bo *tegra_fb_get_plane(struct drm_framebuffer *framebuffer,
bool tegra_fb_is_bottom_up(struct drm_framebuffer *framebuffer); bool tegra_fb_is_bottom_up(struct drm_framebuffer *framebuffer);
int tegra_fb_get_tiling(struct drm_framebuffer *framebuffer, int tegra_fb_get_tiling(struct drm_framebuffer *framebuffer,
struct tegra_bo_tiling *tiling); struct tegra_bo_tiling *tiling);
struct drm_framebuffer *tegra_fb_create(struct drm_device *drm,
struct drm_file *file,
struct drm_mode_fb_cmd2 *cmd);
int tegra_drm_fb_prepare(struct drm_device *drm); int tegra_drm_fb_prepare(struct drm_device *drm);
void tegra_drm_fb_free(struct drm_device *drm); void tegra_drm_fb_free(struct drm_device *drm);
int tegra_drm_fb_init(struct drm_device *drm); int tegra_drm_fb_init(struct drm_device *drm);
void tegra_drm_fb_exit(struct drm_device *drm); void tegra_drm_fb_exit(struct drm_device *drm);
#ifdef CONFIG_DRM_TEGRA_FBDEV #ifdef CONFIG_DRM_TEGRA_FBDEV
void tegra_fbdev_restore_mode(struct tegra_fbdev *fbdev); void tegra_fbdev_restore_mode(struct tegra_fbdev *fbdev);
void tegra_fb_output_poll_changed(struct drm_device *drm);
#endif #endif
extern struct platform_driver tegra_dc_driver; extern struct platform_driver tegra_dc_driver;
......
This diff is collapsed.
...@@ -129,7 +129,7 @@ static struct tegra_fb *tegra_fb_alloc(struct drm_device *drm, ...@@ -129,7 +129,7 @@ static struct tegra_fb *tegra_fb_alloc(struct drm_device *drm,
return fb; return fb;
} }
static struct drm_framebuffer *tegra_fb_create(struct drm_device *drm, struct drm_framebuffer *tegra_fb_create(struct drm_device *drm,
struct drm_file *file, struct drm_file *file,
struct drm_mode_fb_cmd2 *cmd) struct drm_mode_fb_cmd2 *cmd)
{ {
...@@ -377,7 +377,7 @@ void tegra_fbdev_restore_mode(struct tegra_fbdev *fbdev) ...@@ -377,7 +377,7 @@ void tegra_fbdev_restore_mode(struct tegra_fbdev *fbdev)
drm_fb_helper_restore_fbdev_mode_unlocked(&fbdev->base); drm_fb_helper_restore_fbdev_mode_unlocked(&fbdev->base);
} }
static void tegra_fb_output_poll_changed(struct drm_device *drm) void tegra_fb_output_poll_changed(struct drm_device *drm)
{ {
struct tegra_drm *tegra = drm->dev_private; struct tegra_drm *tegra = drm->dev_private;
...@@ -386,28 +386,11 @@ static void tegra_fb_output_poll_changed(struct drm_device *drm) ...@@ -386,28 +386,11 @@ static void tegra_fb_output_poll_changed(struct drm_device *drm)
} }
#endif #endif
static const struct drm_mode_config_funcs tegra_drm_mode_funcs = {
.fb_create = tegra_fb_create,
#ifdef CONFIG_DRM_TEGRA_FBDEV
.output_poll_changed = tegra_fb_output_poll_changed,
#endif
};
int tegra_drm_fb_prepare(struct drm_device *drm) int tegra_drm_fb_prepare(struct drm_device *drm)
{ {
#ifdef CONFIG_DRM_TEGRA_FBDEV #ifdef CONFIG_DRM_TEGRA_FBDEV
struct tegra_drm *tegra = drm->dev_private; struct tegra_drm *tegra = drm->dev_private;
#endif
drm->mode_config.min_width = 0;
drm->mode_config.min_height = 0;
drm->mode_config.max_width = 4096;
drm->mode_config.max_height = 4096;
drm->mode_config.funcs = &tegra_drm_mode_funcs;
#ifdef CONFIG_DRM_TEGRA_FBDEV
tegra->fbdev = tegra_fbdev_create(drm); tegra->fbdev = tegra_fbdev_create(drm);
if (IS_ERR(tegra->fbdev)) if (IS_ERR(tegra->fbdev))
return PTR_ERR(tegra->fbdev); return PTR_ERR(tegra->fbdev);
......
...@@ -92,36 +92,6 @@ static const struct host1x_bo_ops tegra_bo_ops = { ...@@ -92,36 +92,6 @@ static const struct host1x_bo_ops tegra_bo_ops = {
.kunmap = tegra_bo_kunmap, .kunmap = tegra_bo_kunmap,
}; };
/*
* A generic iommu_map_sg() function is being reviewed and will hopefully be
* merged soon. At that point this function can be dropped in favour of the
* one provided by the IOMMU API.
*/
static ssize_t __iommu_map_sg(struct iommu_domain *domain, unsigned long iova,
struct scatterlist *sg, unsigned int nents,
int prot)
{
struct scatterlist *s;
size_t offset = 0;
unsigned int i;
int err;
for_each_sg(sg, s, nents, i) {
phys_addr_t phys = page_to_phys(sg_page(s));
size_t length = s->offset + s->length;
err = iommu_map(domain, iova + offset, phys, length, prot);
if (err < 0) {
iommu_unmap(domain, iova, offset);
return err;
}
offset += length;
}
return offset;
}
static int tegra_bo_iommu_map(struct tegra_drm *tegra, struct tegra_bo *bo) static int tegra_bo_iommu_map(struct tegra_drm *tegra, struct tegra_bo *bo)
{ {
int prot = IOMMU_READ | IOMMU_WRITE; int prot = IOMMU_READ | IOMMU_WRITE;
...@@ -144,7 +114,7 @@ static int tegra_bo_iommu_map(struct tegra_drm *tegra, struct tegra_bo *bo) ...@@ -144,7 +114,7 @@ static int tegra_bo_iommu_map(struct tegra_drm *tegra, struct tegra_bo *bo)
bo->paddr = bo->mm->start; bo->paddr = bo->mm->start;
err = __iommu_map_sg(tegra->domain, bo->paddr, bo->sgt->sgl, err = iommu_map_sg(tegra->domain, bo->paddr, bo->sgt->sgl,
bo->sgt->nents, prot); bo->sgt->nents, prot);
if (err < 0) { if (err < 0) {
dev_err(tegra->drm->dev, "failed to map buffer: %zd\n", err); dev_err(tegra->drm->dev, "failed to map buffer: %zd\n", err);
...@@ -244,10 +214,8 @@ static int tegra_bo_get_pages(struct drm_device *drm, struct tegra_bo *bo) ...@@ -244,10 +214,8 @@ static int tegra_bo_get_pages(struct drm_device *drm, struct tegra_bo *bo)
for_each_sg(sgt->sgl, s, sgt->nents, i) for_each_sg(sgt->sgl, s, sgt->nents, i)
sg_dma_address(s) = sg_phys(s); sg_dma_address(s) = sg_phys(s);
if (dma_map_sg(drm->dev, sgt->sgl, sgt->nents, DMA_TO_DEVICE) == 0) { if (dma_map_sg(drm->dev, sgt->sgl, sgt->nents, DMA_TO_DEVICE) == 0)
sgt = ERR_PTR(-ENOMEM);
goto release_sgt; goto release_sgt;
}
bo->sgt = sgt; bo->sgt = sgt;
...@@ -256,6 +224,7 @@ static int tegra_bo_get_pages(struct drm_device *drm, struct tegra_bo *bo) ...@@ -256,6 +224,7 @@ static int tegra_bo_get_pages(struct drm_device *drm, struct tegra_bo *bo)
release_sgt: release_sgt:
sg_free_table(sgt); sg_free_table(sgt);
kfree(sgt); kfree(sgt);
sgt = ERR_PTR(-ENOMEM);
put_pages: put_pages:
drm_gem_put_pages(&bo->gem, bo->pages, false, false); drm_gem_put_pages(&bo->gem, bo->pages, false, false);
return PTR_ERR(sgt); return PTR_ERR(sgt);
......
This diff is collapsed.
...@@ -12,9 +12,9 @@ ...@@ -12,9 +12,9 @@
#include "mipi-phy.h" #include "mipi-phy.h"
/* /*
* Default D-PHY timings based on MIPI D-PHY specification. Derived from * Default D-PHY timings based on MIPI D-PHY specification. Derived from the
* the valid ranges specified in Section 5.9 of the D-PHY specification * valid ranges specified in Section 6.9, Table 14, Page 40 of the D-PHY
* with minor adjustments. * specification (v1.2) with minor adjustments.
*/ */
int mipi_dphy_timing_get_default(struct mipi_dphy_timing *timing, int mipi_dphy_timing_get_default(struct mipi_dphy_timing *timing,
unsigned long period) unsigned long period)
...@@ -34,7 +34,20 @@ int mipi_dphy_timing_get_default(struct mipi_dphy_timing *timing, ...@@ -34,7 +34,20 @@ int mipi_dphy_timing_get_default(struct mipi_dphy_timing *timing,
timing->hszero = 145 + 5 * period; timing->hszero = 145 + 5 * period;
timing->hssettle = 85 + 6 * period; timing->hssettle = 85 + 6 * period;
timing->hsskip = 40; timing->hsskip = 40;
timing->hstrail = max(8 * period, 60 + 4 * period);
/*
* The MIPI D-PHY specification (Section 6.9, v1.2, Table 14, Page 40)
* contains this formula as:
*
* T_HS-TRAIL = max(n * 8 * period, 60 + n * 4 * period)
*
* where n = 1 for forward-direction HS mode and n = 4 for reverse-
* direction HS mode. There's only one setting and this function does
* not parameterize on anything other that period, so this code will
* assumes that reverse-direction HS mode is supported and uses n = 4.
*/
timing->hstrail = max(4 * 8 * period, 60 + 4 * 4 * period);
timing->init = 100000; timing->init = 100000;
timing->lpx = 60; timing->lpx = 60;
timing->taget = 5 * timing->lpx; timing->taget = 5 * timing->lpx;
...@@ -46,8 +59,8 @@ int mipi_dphy_timing_get_default(struct mipi_dphy_timing *timing, ...@@ -46,8 +59,8 @@ int mipi_dphy_timing_get_default(struct mipi_dphy_timing *timing,
} }
/* /*
* Validate D-PHY timing according to MIPI Alliance Specification for D-PHY, * Validate D-PHY timing according to MIPI D-PHY specification (v1.2, Section
* Section 5.9 "Global Operation Timing Parameters". * Section 6.9 "Global Operation Timing Parameters").
*/ */
int mipi_dphy_timing_validate(struct mipi_dphy_timing *timing, int mipi_dphy_timing_validate(struct mipi_dphy_timing *timing,
unsigned long period) unsigned long period)
......
...@@ -9,10 +9,11 @@ ...@@ -9,10 +9,11 @@
#include <linux/of_gpio.h> #include <linux/of_gpio.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_panel.h> #include <drm/drm_panel.h>
#include "drm.h" #include "drm.h"
static int tegra_connector_get_modes(struct drm_connector *connector) int tegra_output_connector_get_modes(struct drm_connector *connector)
{ {
struct tegra_output *output = connector_to_output(connector); struct tegra_output *output = connector_to_output(connector);
struct edid *edid = NULL; struct edid *edid = NULL;
...@@ -43,43 +44,20 @@ static int tegra_connector_get_modes(struct drm_connector *connector) ...@@ -43,43 +44,20 @@ static int tegra_connector_get_modes(struct drm_connector *connector)
return err; return err;
} }
static int tegra_connector_mode_valid(struct drm_connector *connector, struct drm_encoder *
struct drm_display_mode *mode) tegra_output_connector_best_encoder(struct drm_connector *connector)
{
struct tegra_output *output = connector_to_output(connector);
enum drm_mode_status status = MODE_OK;
int err;
err = tegra_output_check_mode(output, mode, &status);
if (err < 0)
return MODE_ERROR;
return status;
}
static struct drm_encoder *
tegra_connector_best_encoder(struct drm_connector *connector)
{ {
struct tegra_output *output = connector_to_output(connector); struct tegra_output *output = connector_to_output(connector);
return &output->encoder; return &output->encoder;
} }
static const struct drm_connector_helper_funcs connector_helper_funcs = { enum drm_connector_status
.get_modes = tegra_connector_get_modes, tegra_output_connector_detect(struct drm_connector *connector, bool force)
.mode_valid = tegra_connector_mode_valid,
.best_encoder = tegra_connector_best_encoder,
};
static enum drm_connector_status
tegra_connector_detect(struct drm_connector *connector, bool force)
{ {
struct tegra_output *output = connector_to_output(connector); struct tegra_output *output = connector_to_output(connector);
enum drm_connector_status status = connector_status_unknown; enum drm_connector_status status = connector_status_unknown;
if (output->ops->detect)
return output->ops->detect(output);
if (gpio_is_valid(output->hpd_gpio)) { if (gpio_is_valid(output->hpd_gpio)) {
if (gpio_get_value(output->hpd_gpio) == 0) if (gpio_get_value(output->hpd_gpio) == 0)
status = connector_status_disconnected; status = connector_status_disconnected;
...@@ -90,95 +68,22 @@ tegra_connector_detect(struct drm_connector *connector, bool force) ...@@ -90,95 +68,22 @@ tegra_connector_detect(struct drm_connector *connector, bool force)
status = connector_status_disconnected; status = connector_status_disconnected;
else else
status = connector_status_connected; status = connector_status_connected;
if (connector->connector_type == DRM_MODE_CONNECTOR_LVDS)
status = connector_status_connected;
} }
return status; return status;
} }
static void drm_connector_clear(struct drm_connector *connector) void tegra_output_connector_destroy(struct drm_connector *connector)
{
memset(connector, 0, sizeof(*connector));
}
static void tegra_connector_destroy(struct drm_connector *connector)
{ {
drm_connector_unregister(connector); drm_connector_unregister(connector);
drm_connector_cleanup(connector); drm_connector_cleanup(connector);
drm_connector_clear(connector);
} }
static const struct drm_connector_funcs connector_funcs = { void tegra_output_encoder_destroy(struct drm_encoder *encoder)
.dpms = drm_helper_connector_dpms,
.detect = tegra_connector_detect,
.fill_modes = drm_helper_probe_single_connector_modes,
.destroy = tegra_connector_destroy,
};
static void drm_encoder_clear(struct drm_encoder *encoder)
{
memset(encoder, 0, sizeof(*encoder));
}
static void tegra_encoder_destroy(struct drm_encoder *encoder)
{ {
drm_encoder_cleanup(encoder); drm_encoder_cleanup(encoder);
drm_encoder_clear(encoder);
}
static const struct drm_encoder_funcs encoder_funcs = {
.destroy = tegra_encoder_destroy,
};
static void tegra_encoder_dpms(struct drm_encoder *encoder, int mode)
{
struct tegra_output *output = encoder_to_output(encoder);
struct drm_panel *panel = output->panel;
if (mode != DRM_MODE_DPMS_ON) {
drm_panel_disable(panel);
tegra_output_disable(output);
drm_panel_unprepare(panel);
} else {
drm_panel_prepare(panel);
tegra_output_enable(output);
drm_panel_enable(panel);
}
} }
static bool tegra_encoder_mode_fixup(struct drm_encoder *encoder,
const struct drm_display_mode *mode,
struct drm_display_mode *adjusted)
{
return true;
}
static void tegra_encoder_prepare(struct drm_encoder *encoder)
{
tegra_encoder_dpms(encoder, DRM_MODE_DPMS_OFF);
}
static void tegra_encoder_commit(struct drm_encoder *encoder)
{
tegra_encoder_dpms(encoder, DRM_MODE_DPMS_ON);
}
static void tegra_encoder_mode_set(struct drm_encoder *encoder,
struct drm_display_mode *mode,
struct drm_display_mode *adjusted)
{
}
static const struct drm_encoder_helper_funcs encoder_helper_funcs = {
.dpms = tegra_encoder_dpms,
.mode_fixup = tegra_encoder_mode_fixup,
.prepare = tegra_encoder_prepare,
.commit = tegra_encoder_commit,
.mode_set = tegra_encoder_mode_set,
};
static irqreturn_t hpd_irq(int irq, void *data) static irqreturn_t hpd_irq(int irq, void *data)
{ {
struct tegra_output *output = data; struct tegra_output *output = data;
...@@ -268,7 +173,7 @@ int tegra_output_probe(struct tegra_output *output) ...@@ -268,7 +173,7 @@ int tegra_output_probe(struct tegra_output *output)
return 0; return 0;
} }
int tegra_output_remove(struct tegra_output *output) void tegra_output_remove(struct tegra_output *output)
{ {
if (gpio_is_valid(output->hpd_gpio)) { if (gpio_is_valid(output->hpd_gpio)) {
free_irq(output->hpd_irq, output); free_irq(output->hpd_irq, output);
...@@ -277,56 +182,17 @@ int tegra_output_remove(struct tegra_output *output) ...@@ -277,56 +182,17 @@ int tegra_output_remove(struct tegra_output *output)
if (output->ddc) if (output->ddc)
put_device(&output->ddc->dev); put_device(&output->ddc->dev);
return 0;
} }
int tegra_output_init(struct drm_device *drm, struct tegra_output *output) int tegra_output_init(struct drm_device *drm, struct tegra_output *output)
{ {
int connector, encoder; int err;
switch (output->type) {
case TEGRA_OUTPUT_RGB:
connector = DRM_MODE_CONNECTOR_LVDS;
encoder = DRM_MODE_ENCODER_LVDS;
break;
case TEGRA_OUTPUT_HDMI:
connector = DRM_MODE_CONNECTOR_HDMIA;
encoder = DRM_MODE_ENCODER_TMDS;
break;
case TEGRA_OUTPUT_DSI:
connector = DRM_MODE_CONNECTOR_DSI;
encoder = DRM_MODE_ENCODER_DSI;
break;
case TEGRA_OUTPUT_EDP:
connector = DRM_MODE_CONNECTOR_eDP;
encoder = DRM_MODE_ENCODER_TMDS;
break;
default:
connector = DRM_MODE_CONNECTOR_Unknown;
encoder = DRM_MODE_ENCODER_NONE;
break;
}
drm_connector_init(drm, &output->connector, &connector_funcs,
connector);
drm_connector_helper_add(&output->connector, &connector_helper_funcs);
output->connector.dpms = DRM_MODE_DPMS_OFF;
if (output->panel)
drm_panel_attach(output->panel, &output->connector);
drm_encoder_init(drm, &output->encoder, &encoder_funcs, encoder);
drm_encoder_helper_add(&output->encoder, &encoder_helper_funcs);
drm_mode_connector_attach_encoder(&output->connector, &output->encoder);
drm_connector_register(&output->connector);
output->encoder.possible_crtcs = 0x3; if (output->panel) {
err = drm_panel_attach(output->panel, &output->connector);
if (err < 0)
return err;
}
/* /*
* The connector is now registered and ready to receive hotplug events * The connector is now registered and ready to receive hotplug events
...@@ -338,7 +204,7 @@ int tegra_output_init(struct drm_device *drm, struct tegra_output *output) ...@@ -338,7 +204,7 @@ int tegra_output_init(struct drm_device *drm, struct tegra_output *output)
return 0; return 0;
} }
int tegra_output_exit(struct tegra_output *output) void tegra_output_exit(struct tegra_output *output)
{ {
/* /*
* The connector is going away, so the interrupt must be disabled to * The connector is going away, so the interrupt must be disabled to
...@@ -349,6 +215,4 @@ int tegra_output_exit(struct tegra_output *output) ...@@ -349,6 +215,4 @@ int tegra_output_exit(struct tegra_output *output)
if (output->panel) if (output->panel)
drm_panel_detach(output->panel); drm_panel_detach(output->panel);
return 0;
} }
...@@ -9,6 +9,9 @@ ...@@ -9,6 +9,9 @@
#include <linux/clk.h> #include <linux/clk.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_panel.h>
#include "drm.h" #include "drm.h"
#include "dc.h" #include "dc.h"
...@@ -85,13 +88,65 @@ static void tegra_dc_write_regs(struct tegra_dc *dc, ...@@ -85,13 +88,65 @@ static void tegra_dc_write_regs(struct tegra_dc *dc,
tegra_dc_writel(dc, table[i].value, table[i].offset); tegra_dc_writel(dc, table[i].value, table[i].offset);
} }
static int tegra_output_rgb_enable(struct tegra_output *output) static void tegra_rgb_connector_dpms(struct drm_connector *connector,
int mode)
{
}
static const struct drm_connector_funcs tegra_rgb_connector_funcs = {
.dpms = tegra_rgb_connector_dpms,
.reset = drm_atomic_helper_connector_reset,
.detect = tegra_output_connector_detect,
.fill_modes = drm_helper_probe_single_connector_modes,
.destroy = tegra_output_connector_destroy,
.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
};
static enum drm_mode_status
tegra_rgb_connector_mode_valid(struct drm_connector *connector,
struct drm_display_mode *mode)
{
/*
* FIXME: For now, always assume that the mode is okay. There are
* unresolved issues with clk_round_rate(), which doesn't always
* reliably report whether a frequency can be set or not.
*/
return MODE_OK;
}
static const struct drm_connector_helper_funcs tegra_rgb_connector_helper_funcs = {
.get_modes = tegra_output_connector_get_modes,
.mode_valid = tegra_rgb_connector_mode_valid,
.best_encoder = tegra_output_connector_best_encoder,
};
static const struct drm_encoder_funcs tegra_rgb_encoder_funcs = {
.destroy = tegra_output_encoder_destroy,
};
static void tegra_rgb_encoder_dpms(struct drm_encoder *encoder, int mode)
{
}
static void tegra_rgb_encoder_prepare(struct drm_encoder *encoder)
{ {
}
static void tegra_rgb_encoder_commit(struct drm_encoder *encoder)
{
}
static void tegra_rgb_encoder_mode_set(struct drm_encoder *encoder,
struct drm_display_mode *mode,
struct drm_display_mode *adjusted)
{
struct tegra_output *output = encoder_to_output(encoder);
struct tegra_rgb *rgb = to_rgb(output); struct tegra_rgb *rgb = to_rgb(output);
unsigned long value; u32 value;
if (rgb->enabled) if (output->panel)
return 0; drm_panel_prepare(output->panel);
tegra_dc_write_regs(rgb->dc, rgb_enable, ARRAY_SIZE(rgb_enable)); tegra_dc_write_regs(rgb->dc, rgb_enable, ARRAY_SIZE(rgb_enable));
...@@ -113,64 +168,39 @@ static int tegra_output_rgb_enable(struct tegra_output *output) ...@@ -113,64 +168,39 @@ static int tegra_output_rgb_enable(struct tegra_output *output)
value = SC0_H_QUALIFIER_NONE | SC1_H_QUALIFIER_NONE; value = SC0_H_QUALIFIER_NONE | SC1_H_QUALIFIER_NONE;
tegra_dc_writel(rgb->dc, value, DC_DISP_SHIFT_CLOCK_OPTIONS); tegra_dc_writel(rgb->dc, value, DC_DISP_SHIFT_CLOCK_OPTIONS);
value = tegra_dc_readl(rgb->dc, DC_CMD_DISPLAY_COMMAND); tegra_dc_commit(rgb->dc);
value &= ~DISP_CTRL_MODE_MASK;
value |= DISP_CTRL_MODE_C_DISPLAY;
tegra_dc_writel(rgb->dc, value, DC_CMD_DISPLAY_COMMAND);
value = tegra_dc_readl(rgb->dc, DC_CMD_DISPLAY_POWER_CONTROL);
value |= PW0_ENABLE | PW1_ENABLE | PW2_ENABLE | PW3_ENABLE |
PW4_ENABLE | PM0_ENABLE | PM1_ENABLE;
tegra_dc_writel(rgb->dc, value, DC_CMD_DISPLAY_POWER_CONTROL);
tegra_dc_writel(rgb->dc, GENERAL_ACT_REQ << 8, DC_CMD_STATE_CONTROL); if (output->panel)
tegra_dc_writel(rgb->dc, GENERAL_ACT_REQ, DC_CMD_STATE_CONTROL); drm_panel_enable(output->panel);
rgb->enabled = true;
return 0;
} }
static int tegra_output_rgb_disable(struct tegra_output *output) static void tegra_rgb_encoder_disable(struct drm_encoder *encoder)
{ {
struct tegra_output *output = encoder_to_output(encoder);
struct tegra_rgb *rgb = to_rgb(output); struct tegra_rgb *rgb = to_rgb(output);
unsigned long value;
if (!rgb->enabled)
return 0;
value = tegra_dc_readl(rgb->dc, DC_CMD_DISPLAY_POWER_CONTROL);
value &= ~(PW0_ENABLE | PW1_ENABLE | PW2_ENABLE | PW3_ENABLE |
PW4_ENABLE | PM0_ENABLE | PM1_ENABLE);
tegra_dc_writel(rgb->dc, value, DC_CMD_DISPLAY_POWER_CONTROL);
value = tegra_dc_readl(rgb->dc, DC_CMD_DISPLAY_COMMAND);
value &= ~DISP_CTRL_MODE_MASK;
tegra_dc_writel(rgb->dc, value, DC_CMD_DISPLAY_COMMAND);
tegra_dc_writel(rgb->dc, GENERAL_ACT_REQ << 8, DC_CMD_STATE_CONTROL); if (output->panel)
tegra_dc_writel(rgb->dc, GENERAL_ACT_REQ, DC_CMD_STATE_CONTROL); drm_panel_disable(output->panel);
tegra_dc_write_regs(rgb->dc, rgb_disable, ARRAY_SIZE(rgb_disable)); tegra_dc_write_regs(rgb->dc, rgb_disable, ARRAY_SIZE(rgb_disable));
tegra_dc_commit(rgb->dc);
rgb->enabled = false; if (output->panel)
drm_panel_unprepare(output->panel);
return 0;
} }
static int tegra_output_rgb_setup_clock(struct tegra_output *output, static int
struct clk *clk, unsigned long pclk, tegra_rgb_encoder_atomic_check(struct drm_encoder *encoder,
unsigned int *div) struct drm_crtc_state *crtc_state,
struct drm_connector_state *conn_state)
{ {
struct tegra_output *output = encoder_to_output(encoder);
struct tegra_dc *dc = to_tegra_dc(conn_state->crtc);
unsigned long pclk = crtc_state->mode.clock * 1000;
struct tegra_rgb *rgb = to_rgb(output); struct tegra_rgb *rgb = to_rgb(output);
unsigned int div;
int err; int err;
err = clk_set_parent(clk, rgb->clk_parent);
if (err < 0) {
dev_err(output->dev, "failed to set parent: %d\n", err);
return err;
}
/* /*
* We may not want to change the frequency of the parent clock, since * We may not want to change the frequency of the parent clock, since
* it may be a parent for other peripherals. This is due to the fact * it may be a parent for other peripherals. This is due to the fact
...@@ -187,32 +217,26 @@ static int tegra_output_rgb_setup_clock(struct tegra_output *output, ...@@ -187,32 +217,26 @@ static int tegra_output_rgb_setup_clock(struct tegra_output *output,
* and hope that the desired frequency can be matched (or at least * and hope that the desired frequency can be matched (or at least
* matched sufficiently close that the panel will still work). * matched sufficiently close that the panel will still work).
*/ */
div = ((clk_get_rate(rgb->clk) * 2) / pclk) - 2;
pclk = 0;
*div = ((clk_get_rate(clk) * 2) / pclk) - 2; err = tegra_dc_state_setup_clock(dc, crtc_state, rgb->clk_parent,
pclk, div);
return 0; if (err < 0) {
} dev_err(output->dev, "failed to setup CRTC state: %d\n", err);
return err;
static int tegra_output_rgb_check_mode(struct tegra_output *output, }
struct drm_display_mode *mode,
enum drm_mode_status *status)
{
/*
* FIXME: For now, always assume that the mode is okay. There are
* unresolved issues with clk_round_rate(), which doesn't always
* reliably report whether a frequency can be set or not.
*/
*status = MODE_OK;
return 0; return err;
} }
static const struct tegra_output_ops rgb_ops = { static const struct drm_encoder_helper_funcs tegra_rgb_encoder_helper_funcs = {
.enable = tegra_output_rgb_enable, .dpms = tegra_rgb_encoder_dpms,
.disable = tegra_output_rgb_disable, .prepare = tegra_rgb_encoder_prepare,
.setup_clock = tegra_output_rgb_setup_clock, .commit = tegra_rgb_encoder_commit,
.check_mode = tegra_output_rgb_check_mode, .mode_set = tegra_rgb_encoder_mode_set,
.disable = tegra_rgb_encoder_disable,
.atomic_check = tegra_rgb_encoder_atomic_check,
}; };
int tegra_dc_rgb_probe(struct tegra_dc *dc) int tegra_dc_rgb_probe(struct tegra_dc *dc)
...@@ -262,64 +286,58 @@ int tegra_dc_rgb_probe(struct tegra_dc *dc) ...@@ -262,64 +286,58 @@ int tegra_dc_rgb_probe(struct tegra_dc *dc)
int tegra_dc_rgb_remove(struct tegra_dc *dc) int tegra_dc_rgb_remove(struct tegra_dc *dc)
{ {
int err;
if (!dc->rgb) if (!dc->rgb)
return 0; return 0;
err = tegra_output_remove(dc->rgb); tegra_output_remove(dc->rgb);
if (err < 0) dc->rgb = NULL;
return err;
return 0; return 0;
} }
int tegra_dc_rgb_init(struct drm_device *drm, struct tegra_dc *dc) int tegra_dc_rgb_init(struct drm_device *drm, struct tegra_dc *dc)
{ {
struct tegra_rgb *rgb = to_rgb(dc->rgb); struct tegra_output *output = dc->rgb;
int err; int err;
if (!dc->rgb) if (!dc->rgb)
return -ENODEV; return -ENODEV;
rgb->output.type = TEGRA_OUTPUT_RGB; drm_connector_init(drm, &output->connector, &tegra_rgb_connector_funcs,
rgb->output.ops = &rgb_ops; DRM_MODE_CONNECTOR_LVDS);
drm_connector_helper_add(&output->connector,
&tegra_rgb_connector_helper_funcs);
output->connector.dpms = DRM_MODE_DPMS_OFF;
drm_encoder_init(drm, &output->encoder, &tegra_rgb_encoder_funcs,
DRM_MODE_ENCODER_LVDS);
drm_encoder_helper_add(&output->encoder,
&tegra_rgb_encoder_helper_funcs);
drm_mode_connector_attach_encoder(&output->connector,
&output->encoder);
drm_connector_register(&output->connector);
err = tegra_output_init(dc->base.dev, &rgb->output); err = tegra_output_init(drm, output);
if (err < 0) { if (err < 0) {
dev_err(dc->dev, "output setup failed: %d\n", err); dev_err(output->dev, "failed to initialize output: %d\n", err);
return err; return err;
} }
/* /*
* By default, outputs can be associated with each display controller. * Other outputs can be attached to either display controller. The RGB
* RGB outputs are an exception, so we make sure they can be attached * outputs are an exception and work only with their parent display
* to only their parent display controller. * controller.
*/ */
rgb->output.encoder.possible_crtcs = drm_crtc_mask(&dc->base); output->encoder.possible_crtcs = drm_crtc_mask(&dc->base);
return 0; return 0;
} }
int tegra_dc_rgb_exit(struct tegra_dc *dc) int tegra_dc_rgb_exit(struct tegra_dc *dc)
{ {
if (dc->rgb) { if (dc->rgb)
int err; tegra_output_exit(dc->rgb);
err = tegra_output_disable(dc->rgb);
if (err < 0) {
dev_err(dc->dev, "output failed to disable: %d\n", err);
return err;
}
err = tegra_output_exit(dc->rgb);
if (err < 0) {
dev_err(dc->dev, "output cleanup failed: %d\n", err);
return err;
}
dc->rgb = NULL;
}
return 0; return 0;
} }
This diff is collapsed.
This diff is collapsed.
...@@ -18,10 +18,10 @@ ...@@ -18,10 +18,10 @@
#ifndef HOST1X_BUS_H #ifndef HOST1X_BUS_H
#define HOST1X_BUS_H #define HOST1X_BUS_H
struct bus_type;
struct host1x; struct host1x;
int host1x_bus_init(void); extern struct bus_type host1x_bus_type;
void host1x_bus_exit(void);
int host1x_register(struct host1x *host1x); int host1x_register(struct host1x *host1x);
int host1x_unregister(struct host1x *host1x); int host1x_unregister(struct host1x *host1x);
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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