You've already forked linux-apfs
mirror of
https://github.com/linux-apfs/linux-apfs.git
synced 2026-05-01 15:00:59 -07:00
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 ...
This commit is contained in:
@@ -1,3 +1,6 @@
|
||||
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-y += drm/ vga/
|
||||
obj-$(CONFIG_IMX_IPUV3_CORE) += ipu-v3/
|
||||
|
||||
@@ -297,13 +297,22 @@ mode_fixup(struct drm_atomic_state *state)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ret = funcs->mode_fixup(encoder, &crtc_state->mode,
|
||||
&crtc_state->adjusted_mode);
|
||||
if (!ret) {
|
||||
DRM_DEBUG_KMS("[ENCODER:%d:%s] fixup failed\n",
|
||||
encoder->base.id, encoder->name);
|
||||
return -EINVAL;
|
||||
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,
|
||||
&crtc_state->adjusted_mode);
|
||||
if (!ret) {
|
||||
DRM_DEBUG_KMS("[ENCODER:%d:%s] fixup failed\n",
|
||||
encoder->base.id, encoder->name);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1108,12 +1117,19 @@ void drm_atomic_helper_commit_planes(struct drm_device *dev,
|
||||
|
||||
funcs = plane->helper_private;
|
||||
|
||||
if (!funcs || !funcs->atomic_update)
|
||||
if (!funcs)
|
||||
continue;
|
||||
|
||||
old_plane_state = old_state->plane_states[i];
|
||||
|
||||
funcs->atomic_update(plane, old_plane_state);
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
|
||||
for (i = 0; i < ncrtcs; i++) {
|
||||
|
||||
@@ -449,7 +449,15 @@ int drm_plane_helper_commit(struct drm_plane *plane,
|
||||
crtc_funcs[i]->atomic_begin(crtc[i]);
|
||||
}
|
||||
|
||||
plane_funcs->atomic_update(plane, plane_state);
|
||||
/*
|
||||
* 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);
|
||||
|
||||
for (i = 0; i < 2; i++) {
|
||||
if (crtc_funcs[i] && crtc_funcs[i]->atomic_flush)
|
||||
|
||||
+595
-382
File diff suppressed because it is too large
Load Diff
+135
-5
@@ -10,6 +10,9 @@
|
||||
#include <linux/host1x.h>
|
||||
#include <linux/iommu.h>
|
||||
|
||||
#include <drm/drm_atomic.h>
|
||||
#include <drm/drm_atomic_helper.h>
|
||||
|
||||
#include "drm.h"
|
||||
#include "gem.h"
|
||||
|
||||
@@ -24,6 +27,92 @@ struct tegra_drm_file {
|
||||
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)
|
||||
{
|
||||
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)
|
||||
|
||||
if (iommu_present(&platform_bus_type)) {
|
||||
tegra->domain = iommu_domain_alloc(&platform_bus_type);
|
||||
if (IS_ERR(tegra->domain)) {
|
||||
err = PTR_ERR(tegra->domain);
|
||||
if (!tegra->domain) {
|
||||
err = -ENOMEM;
|
||||
goto free;
|
||||
}
|
||||
|
||||
@@ -47,11 +136,23 @@ static int tegra_drm_load(struct drm_device *drm, unsigned long flags)
|
||||
|
||||
mutex_init(&tegra->clients_lock);
|
||||
INIT_LIST_HEAD(&tegra->clients);
|
||||
|
||||
mutex_init(&tegra->commit.lock);
|
||||
INIT_WORK(&tegra->commit.work, tegra_atomic_work);
|
||||
|
||||
drm->dev_private = tegra;
|
||||
tegra->drm = 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);
|
||||
if (err < 0)
|
||||
goto config;
|
||||
@@ -62,6 +163,8 @@ static int tegra_drm_load(struct drm_device *drm, unsigned long flags)
|
||||
if (err < 0)
|
||||
goto fbdev;
|
||||
|
||||
drm_mode_config_reset(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
|
||||
@@ -106,8 +209,8 @@ static int tegra_drm_unload(struct drm_device *drm)
|
||||
|
||||
drm_kms_helper_poll_fini(drm);
|
||||
tegra_drm_fb_exit(drm);
|
||||
drm_vblank_cleanup(drm);
|
||||
drm_mode_config_cleanup(drm);
|
||||
drm_vblank_cleanup(drm);
|
||||
|
||||
err = host1x_device_exit(device);
|
||||
if (err < 0)
|
||||
@@ -190,7 +293,7 @@ static int host1x_reloc_copy_from_user(struct host1x_reloc *dest,
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = get_user(dest->target.offset, &src->cmdbuf.offset);
|
||||
err = get_user(dest->target.offset, &src->target.offset);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
@@ -893,6 +996,30 @@ static int host1x_drm_remove(struct host1x_device *dev)
|
||||
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[] = {
|
||||
{ .compatible = "nvidia,tegra20-dc", },
|
||||
{ .compatible = "nvidia,tegra20-hdmi", },
|
||||
@@ -912,7 +1039,10 @@ static const struct of_device_id host1x_drm_subdevs[] = {
|
||||
};
|
||||
|
||||
static struct host1x_driver host1x_drm_driver = {
|
||||
.name = "drm",
|
||||
.driver = {
|
||||
.name = "drm",
|
||||
.pm = &host1x_drm_pm_ops,
|
||||
},
|
||||
.probe = host1x_drm_probe,
|
||||
.remove = host1x_drm_remove,
|
||||
.subdevs = host1x_drm_subdevs,
|
||||
|
||||
+30
-61
@@ -50,6 +50,12 @@ struct tegra_drm {
|
||||
#endif
|
||||
|
||||
unsigned int pitch_align;
|
||||
|
||||
struct {
|
||||
struct drm_atomic_state *state;
|
||||
struct work_struct work;
|
||||
struct mutex lock;
|
||||
} commit;
|
||||
};
|
||||
|
||||
struct tegra_drm_client;
|
||||
@@ -164,45 +170,31 @@ struct tegra_dc_window {
|
||||
unsigned int h;
|
||||
} dst;
|
||||
unsigned int bits_per_pixel;
|
||||
unsigned int format;
|
||||
unsigned int swap;
|
||||
unsigned int stride[2];
|
||||
unsigned long base[3];
|
||||
bool bottom_up;
|
||||
|
||||
struct tegra_bo_tiling tiling;
|
||||
u32 format;
|
||||
u32 swap;
|
||||
};
|
||||
|
||||
/* from dc.c */
|
||||
void tegra_dc_enable_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);
|
||||
|
||||
struct tegra_output_ops {
|
||||
int (*enable)(struct tegra_output *output);
|
||||
int (*disable)(struct tegra_output *output);
|
||||
int (*setup_clock)(struct tegra_output *output, struct clk *clk,
|
||||
unsigned long pclk, unsigned int *div);
|
||||
int (*check_mode)(struct tegra_output *output,
|
||||
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,
|
||||
};
|
||||
void tegra_dc_commit(struct tegra_dc *dc);
|
||||
int tegra_dc_setup_clock(struct tegra_dc *dc, struct clk *parent,
|
||||
unsigned long pclk, unsigned int div);
|
||||
int tegra_dc_state_setup_clock(struct tegra_dc *dc,
|
||||
struct drm_crtc_state *crtc_state,
|
||||
struct clk *clk, unsigned long pclk,
|
||||
unsigned int div);
|
||||
|
||||
struct tegra_output {
|
||||
struct device_node *of_node;
|
||||
struct device *dev;
|
||||
|
||||
const struct tegra_output_ops *ops;
|
||||
enum tegra_output_type type;
|
||||
|
||||
struct drm_panel *panel;
|
||||
struct i2c_adapter *ddc;
|
||||
const struct edid *edid;
|
||||
@@ -223,42 +215,6 @@ static inline struct tegra_output *connector_to_output(struct drm_connector *c)
|
||||
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 */
|
||||
int tegra_dc_rgb_probe(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);
|
||||
|
||||
/* from output.c */
|
||||
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_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 */
|
||||
struct tegra_dpaux;
|
||||
@@ -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);
|
||||
int tegra_fb_get_tiling(struct drm_framebuffer *framebuffer,
|
||||
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);
|
||||
void tegra_drm_fb_free(struct drm_device *drm);
|
||||
int tegra_drm_fb_init(struct drm_device *drm);
|
||||
void tegra_drm_fb_exit(struct drm_device *drm);
|
||||
#ifdef CONFIG_DRM_TEGRA_FBDEV
|
||||
void tegra_fbdev_restore_mode(struct tegra_fbdev *fbdev);
|
||||
void tegra_fb_output_poll_changed(struct drm_device *drm);
|
||||
#endif
|
||||
|
||||
extern struct platform_driver tegra_dc_driver;
|
||||
|
||||
+350
-274
File diff suppressed because it is too large
Load Diff
@@ -129,9 +129,9 @@ static struct tegra_fb *tegra_fb_alloc(struct drm_device *drm,
|
||||
return fb;
|
||||
}
|
||||
|
||||
static struct drm_framebuffer *tegra_fb_create(struct drm_device *drm,
|
||||
struct drm_file *file,
|
||||
struct drm_mode_fb_cmd2 *cmd)
|
||||
struct drm_framebuffer *tegra_fb_create(struct drm_device *drm,
|
||||
struct drm_file *file,
|
||||
struct drm_mode_fb_cmd2 *cmd)
|
||||
{
|
||||
unsigned int hsub, vsub, i;
|
||||
struct tegra_bo *planes[4];
|
||||
@@ -377,7 +377,7 @@ void tegra_fbdev_restore_mode(struct tegra_fbdev *fbdev)
|
||||
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;
|
||||
|
||||
@@ -386,28 +386,11 @@ static void tegra_fb_output_poll_changed(struct drm_device *drm)
|
||||
}
|
||||
#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)
|
||||
{
|
||||
#ifdef CONFIG_DRM_TEGRA_FBDEV
|
||||
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);
|
||||
if (IS_ERR(tegra->fbdev))
|
||||
return PTR_ERR(tegra->fbdev);
|
||||
|
||||
@@ -92,36 +92,6 @@ static const struct host1x_bo_ops tegra_bo_ops = {
|
||||
.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)
|
||||
{
|
||||
int prot = IOMMU_READ | IOMMU_WRITE;
|
||||
@@ -144,8 +114,8 @@ static int tegra_bo_iommu_map(struct tegra_drm *tegra, struct tegra_bo *bo)
|
||||
|
||||
bo->paddr = bo->mm->start;
|
||||
|
||||
err = __iommu_map_sg(tegra->domain, bo->paddr, bo->sgt->sgl,
|
||||
bo->sgt->nents, prot);
|
||||
err = iommu_map_sg(tegra->domain, bo->paddr, bo->sgt->sgl,
|
||||
bo->sgt->nents, prot);
|
||||
if (err < 0) {
|
||||
dev_err(tegra->drm->dev, "failed to map buffer: %zd\n", err);
|
||||
goto remove;
|
||||
@@ -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)
|
||||
sg_dma_address(s) = sg_phys(s);
|
||||
|
||||
if (dma_map_sg(drm->dev, sgt->sgl, sgt->nents, DMA_TO_DEVICE) == 0) {
|
||||
sgt = ERR_PTR(-ENOMEM);
|
||||
if (dma_map_sg(drm->dev, sgt->sgl, sgt->nents, DMA_TO_DEVICE) == 0)
|
||||
goto release_sgt;
|
||||
}
|
||||
|
||||
bo->sgt = sgt;
|
||||
|
||||
@@ -256,6 +224,7 @@ static int tegra_bo_get_pages(struct drm_device *drm, struct tegra_bo *bo)
|
||||
release_sgt:
|
||||
sg_free_table(sgt);
|
||||
kfree(sgt);
|
||||
sgt = ERR_PTR(-ENOMEM);
|
||||
put_pages:
|
||||
drm_gem_put_pages(&bo->gem, bo->pages, false, false);
|
||||
return PTR_ERR(sgt);
|
||||
|
||||
+159
-170
File diff suppressed because it is too large
Load Diff
@@ -12,9 +12,9 @@
|
||||
#include "mipi-phy.h"
|
||||
|
||||
/*
|
||||
* Default D-PHY timings based on MIPI D-PHY specification. Derived from
|
||||
* the valid ranges specified in Section 5.9 of the D-PHY specification
|
||||
* with minor adjustments.
|
||||
* Default D-PHY timings based on MIPI D-PHY specification. Derived from the
|
||||
* valid ranges specified in Section 6.9, Table 14, Page 40 of the D-PHY
|
||||
* specification (v1.2) with minor adjustments.
|
||||
*/
|
||||
int mipi_dphy_timing_get_default(struct mipi_dphy_timing *timing,
|
||||
unsigned long period)
|
||||
@@ -34,7 +34,20 @@ int mipi_dphy_timing_get_default(struct mipi_dphy_timing *timing,
|
||||
timing->hszero = 145 + 5 * period;
|
||||
timing->hssettle = 85 + 6 * period;
|
||||
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->lpx = 60;
|
||||
timing->taget = 5 * timing->lpx;
|
||||
@@ -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,
|
||||
* Section 5.9 "Global Operation Timing Parameters".
|
||||
* Validate D-PHY timing according to MIPI D-PHY specification (v1.2, Section
|
||||
* Section 6.9 "Global Operation Timing Parameters").
|
||||
*/
|
||||
int mipi_dphy_timing_validate(struct mipi_dphy_timing *timing,
|
||||
unsigned long period)
|
||||
|
||||
+15
-151
@@ -9,10 +9,11 @@
|
||||
|
||||
#include <linux/of_gpio.h>
|
||||
|
||||
#include <drm/drm_atomic_helper.h>
|
||||
#include <drm/drm_panel.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 edid *edid = NULL;
|
||||
@@ -43,43 +44,20 @@ static int tegra_connector_get_modes(struct drm_connector *connector)
|
||||
return err;
|
||||
}
|
||||
|
||||
static int tegra_connector_mode_valid(struct drm_connector *connector,
|
||||
struct drm_display_mode *mode)
|
||||
{
|
||||
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 drm_encoder *
|
||||
tegra_output_connector_best_encoder(struct drm_connector *connector)
|
||||
{
|
||||
struct tegra_output *output = connector_to_output(connector);
|
||||
|
||||
return &output->encoder;
|
||||
}
|
||||
|
||||
static const struct drm_connector_helper_funcs connector_helper_funcs = {
|
||||
.get_modes = tegra_connector_get_modes,
|
||||
.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)
|
||||
enum drm_connector_status
|
||||
tegra_output_connector_detect(struct drm_connector *connector, bool force)
|
||||
{
|
||||
struct tegra_output *output = connector_to_output(connector);
|
||||
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_get_value(output->hpd_gpio) == 0)
|
||||
status = connector_status_disconnected;
|
||||
@@ -90,95 +68,22 @@ tegra_connector_detect(struct drm_connector *connector, bool force)
|
||||
status = connector_status_disconnected;
|
||||
else
|
||||
status = connector_status_connected;
|
||||
|
||||
if (connector->connector_type == DRM_MODE_CONNECTOR_LVDS)
|
||||
status = connector_status_connected;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static void drm_connector_clear(struct drm_connector *connector)
|
||||
{
|
||||
memset(connector, 0, sizeof(*connector));
|
||||
}
|
||||
|
||||
static void tegra_connector_destroy(struct drm_connector *connector)
|
||||
void tegra_output_connector_destroy(struct drm_connector *connector)
|
||||
{
|
||||
drm_connector_unregister(connector);
|
||||
drm_connector_cleanup(connector);
|
||||
drm_connector_clear(connector);
|
||||
}
|
||||
|
||||
static const struct drm_connector_funcs connector_funcs = {
|
||||
.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)
|
||||
void tegra_output_encoder_destroy(struct drm_encoder *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)
|
||||
{
|
||||
struct tegra_output *output = data;
|
||||
@@ -268,7 +173,7 @@ int tegra_output_probe(struct tegra_output *output)
|
||||
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)) {
|
||||
free_irq(output->hpd_irq, output);
|
||||
@@ -277,57 +182,18 @@ int tegra_output_remove(struct tegra_output *output)
|
||||
|
||||
if (output->ddc)
|
||||
put_device(&output->ddc->dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
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;
|
||||
if (output->panel) {
|
||||
err = drm_panel_attach(output->panel, &output->connector);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
/*
|
||||
* The connector is now registered and ready to receive hotplug events
|
||||
* so the hotplug interrupt can be enabled.
|
||||
@@ -338,7 +204,7 @@ int tegra_output_init(struct drm_device *drm, struct tegra_output *output)
|
||||
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
|
||||
@@ -349,6 +215,4 @@ int tegra_output_exit(struct tegra_output *output)
|
||||
|
||||
if (output->panel)
|
||||
drm_panel_detach(output->panel);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
+119
-101
@@ -9,6 +9,9 @@
|
||||
|
||||
#include <linux/clk.h>
|
||||
|
||||
#include <drm/drm_atomic_helper.h>
|
||||
#include <drm/drm_panel.h>
|
||||
|
||||
#include "drm.h"
|
||||
#include "dc.h"
|
||||
|
||||
@@ -85,13 +88,65 @@ static void tegra_dc_write_regs(struct tegra_dc *dc,
|
||||
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)
|
||||
{
|
||||
struct tegra_rgb *rgb = to_rgb(output);
|
||||
unsigned long value;
|
||||
}
|
||||
|
||||
if (rgb->enabled)
|
||||
return 0;
|
||||
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);
|
||||
u32 value;
|
||||
|
||||
if (output->panel)
|
||||
drm_panel_prepare(output->panel);
|
||||
|
||||
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)
|
||||
value = SC0_H_QUALIFIER_NONE | SC1_H_QUALIFIER_NONE;
|
||||
tegra_dc_writel(rgb->dc, value, DC_DISP_SHIFT_CLOCK_OPTIONS);
|
||||
|
||||
value = tegra_dc_readl(rgb->dc, DC_CMD_DISPLAY_COMMAND);
|
||||
value &= ~DISP_CTRL_MODE_MASK;
|
||||
value |= DISP_CTRL_MODE_C_DISPLAY;
|
||||
tegra_dc_writel(rgb->dc, value, DC_CMD_DISPLAY_COMMAND);
|
||||
tegra_dc_commit(rgb->dc);
|
||||
|
||||
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);
|
||||
tegra_dc_writel(rgb->dc, GENERAL_ACT_REQ, DC_CMD_STATE_CONTROL);
|
||||
|
||||
rgb->enabled = true;
|
||||
|
||||
return 0;
|
||||
if (output->panel)
|
||||
drm_panel_enable(output->panel);
|
||||
}
|
||||
|
||||
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);
|
||||
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);
|
||||
tegra_dc_writel(rgb->dc, GENERAL_ACT_REQ, DC_CMD_STATE_CONTROL);
|
||||
if (output->panel)
|
||||
drm_panel_disable(output->panel);
|
||||
|
||||
tegra_dc_write_regs(rgb->dc, rgb_disable, ARRAY_SIZE(rgb_disable));
|
||||
tegra_dc_commit(rgb->dc);
|
||||
|
||||
rgb->enabled = false;
|
||||
|
||||
return 0;
|
||||
if (output->panel)
|
||||
drm_panel_unprepare(output->panel);
|
||||
}
|
||||
|
||||
static int tegra_output_rgb_setup_clock(struct tegra_output *output,
|
||||
struct clk *clk, unsigned long pclk,
|
||||
unsigned int *div)
|
||||
static int
|
||||
tegra_rgb_encoder_atomic_check(struct drm_encoder *encoder,
|
||||
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);
|
||||
unsigned int div;
|
||||
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
|
||||
* 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,
|
||||
* and hope that the desired frequency can be matched (or at least
|
||||
* 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);
|
||||
if (err < 0) {
|
||||
dev_err(output->dev, "failed to setup CRTC state: %d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
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;
|
||||
}
|
||||
|
||||
static const struct tegra_output_ops rgb_ops = {
|
||||
.enable = tegra_output_rgb_enable,
|
||||
.disable = tegra_output_rgb_disable,
|
||||
.setup_clock = tegra_output_rgb_setup_clock,
|
||||
.check_mode = tegra_output_rgb_check_mode,
|
||||
static const struct drm_encoder_helper_funcs tegra_rgb_encoder_helper_funcs = {
|
||||
.dpms = tegra_rgb_encoder_dpms,
|
||||
.prepare = tegra_rgb_encoder_prepare,
|
||||
.commit = tegra_rgb_encoder_commit,
|
||||
.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)
|
||||
@@ -262,64 +286,58 @@ int tegra_dc_rgb_probe(struct tegra_dc *dc)
|
||||
|
||||
int tegra_dc_rgb_remove(struct tegra_dc *dc)
|
||||
{
|
||||
int err;
|
||||
|
||||
if (!dc->rgb)
|
||||
return 0;
|
||||
|
||||
err = tegra_output_remove(dc->rgb);
|
||||
if (err < 0)
|
||||
return err;
|
||||
tegra_output_remove(dc->rgb);
|
||||
dc->rgb = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
if (!dc->rgb)
|
||||
return -ENODEV;
|
||||
|
||||
rgb->output.type = TEGRA_OUTPUT_RGB;
|
||||
rgb->output.ops = &rgb_ops;
|
||||
drm_connector_init(drm, &output->connector, &tegra_rgb_connector_funcs,
|
||||
DRM_MODE_CONNECTOR_LVDS);
|
||||
drm_connector_helper_add(&output->connector,
|
||||
&tegra_rgb_connector_helper_funcs);
|
||||
output->connector.dpms = DRM_MODE_DPMS_OFF;
|
||||
|
||||
err = tegra_output_init(dc->base.dev, &rgb->output);
|
||||
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(drm, output);
|
||||
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;
|
||||
}
|
||||
|
||||
/*
|
||||
* By default, outputs can be associated with each display controller.
|
||||
* RGB outputs are an exception, so we make sure they can be attached
|
||||
* to only their parent display controller.
|
||||
* Other outputs can be attached to either display controller. The RGB
|
||||
* outputs are an exception and work only with their parent display
|
||||
* controller.
|
||||
*/
|
||||
rgb->output.encoder.possible_crtcs = drm_crtc_mask(&dc->base);
|
||||
output->encoder.possible_crtcs = drm_crtc_mask(&dc->base);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int tegra_dc_rgb_exit(struct tegra_dc *dc)
|
||||
{
|
||||
if (dc->rgb) {
|
||||
int err;
|
||||
|
||||
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;
|
||||
}
|
||||
if (dc->rgb)
|
||||
tegra_output_exit(dc->rgb);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
+392
-367
File diff suppressed because it is too large
Load Diff
+143
-94
@@ -72,13 +72,14 @@ static void host1x_subdev_del(struct host1x_subdev *subdev)
|
||||
/**
|
||||
* host1x_device_parse_dt() - scan device tree and add matching subdevices
|
||||
*/
|
||||
static int host1x_device_parse_dt(struct host1x_device *device)
|
||||
static int host1x_device_parse_dt(struct host1x_device *device,
|
||||
struct host1x_driver *driver)
|
||||
{
|
||||
struct device_node *np;
|
||||
int err;
|
||||
|
||||
for_each_child_of_node(device->dev.parent->of_node, np) {
|
||||
if (of_match_node(device->driver->subdevs, np) &&
|
||||
if (of_match_node(driver->subdevs, np) &&
|
||||
of_device_is_available(np)) {
|
||||
err = host1x_subdev_add(device, np);
|
||||
if (err < 0)
|
||||
@@ -109,14 +110,12 @@ static void host1x_subdev_register(struct host1x_device *device,
|
||||
mutex_unlock(&device->clients_lock);
|
||||
mutex_unlock(&device->subdevs_lock);
|
||||
|
||||
/*
|
||||
* When all subdevices have been registered, the composite device is
|
||||
* ready to be probed.
|
||||
*/
|
||||
if (list_empty(&device->subdevs)) {
|
||||
err = device->driver->probe(device);
|
||||
err = device_add(&device->dev);
|
||||
if (err < 0)
|
||||
dev_err(&device->dev, "probe failed: %d\n", err);
|
||||
dev_err(&device->dev, "failed to add: %d\n", err);
|
||||
else
|
||||
device->registered = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -124,16 +123,16 @@ static void __host1x_subdev_unregister(struct host1x_device *device,
|
||||
struct host1x_subdev *subdev)
|
||||
{
|
||||
struct host1x_client *client = subdev->client;
|
||||
int err;
|
||||
|
||||
/*
|
||||
* If all subdevices have been activated, we're about to remove the
|
||||
* first active subdevice, so unload the driver first.
|
||||
*/
|
||||
if (list_empty(&device->subdevs)) {
|
||||
err = device->driver->remove(device);
|
||||
if (err < 0)
|
||||
dev_err(&device->dev, "remove failed: %d\n", err);
|
||||
if (device->registered) {
|
||||
device->registered = false;
|
||||
device_del(&device->dev);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -260,92 +259,61 @@ static int host1x_del_client(struct host1x *host1x,
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static struct bus_type host1x_bus_type = {
|
||||
.name = "host1x",
|
||||
};
|
||||
|
||||
int host1x_bus_init(void)
|
||||
static int host1x_device_match(struct device *dev, struct device_driver *drv)
|
||||
{
|
||||
return bus_register(&host1x_bus_type);
|
||||
return strcmp(dev_name(dev), drv->name) == 0;
|
||||
}
|
||||
|
||||
void host1x_bus_exit(void)
|
||||
{
|
||||
bus_unregister(&host1x_bus_type);
|
||||
}
|
||||
|
||||
static void host1x_device_release(struct device *dev)
|
||||
static int host1x_device_probe(struct device *dev)
|
||||
{
|
||||
struct host1x_driver *driver = to_host1x_driver(dev->driver);
|
||||
struct host1x_device *device = to_host1x_device(dev);
|
||||
|
||||
kfree(device);
|
||||
}
|
||||
|
||||
static int host1x_device_add(struct host1x *host1x,
|
||||
struct host1x_driver *driver)
|
||||
{
|
||||
struct host1x_client *client, *tmp;
|
||||
struct host1x_subdev *subdev;
|
||||
struct host1x_device *device;
|
||||
int err;
|
||||
|
||||
device = kzalloc(sizeof(*device), GFP_KERNEL);
|
||||
if (!device)
|
||||
return -ENOMEM;
|
||||
|
||||
mutex_init(&device->subdevs_lock);
|
||||
INIT_LIST_HEAD(&device->subdevs);
|
||||
INIT_LIST_HEAD(&device->active);
|
||||
mutex_init(&device->clients_lock);
|
||||
INIT_LIST_HEAD(&device->clients);
|
||||
INIT_LIST_HEAD(&device->list);
|
||||
device->driver = driver;
|
||||
|
||||
device->dev.coherent_dma_mask = host1x->dev->coherent_dma_mask;
|
||||
device->dev.dma_mask = &device->dev.coherent_dma_mask;
|
||||
device->dev.release = host1x_device_release;
|
||||
dev_set_name(&device->dev, "%s", driver->name);
|
||||
device->dev.bus = &host1x_bus_type;
|
||||
device->dev.parent = host1x->dev;
|
||||
|
||||
err = device_register(&device->dev);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = host1x_device_parse_dt(device);
|
||||
if (err < 0) {
|
||||
device_unregister(&device->dev);
|
||||
return err;
|
||||
}
|
||||
|
||||
mutex_lock(&host1x->devices_lock);
|
||||
list_add_tail(&device->list, &host1x->devices);
|
||||
mutex_unlock(&host1x->devices_lock);
|
||||
|
||||
mutex_lock(&clients_lock);
|
||||
|
||||
list_for_each_entry_safe(client, tmp, &clients, list) {
|
||||
list_for_each_entry(subdev, &device->subdevs, list) {
|
||||
if (subdev->np == client->dev->of_node) {
|
||||
host1x_subdev_register(device, subdev, client);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mutex_unlock(&clients_lock);
|
||||
if (driver->probe)
|
||||
return driver->probe(device);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Removes a device by first unregistering any subdevices and then removing
|
||||
* itself from the list of devices.
|
||||
*
|
||||
* This function must be called with the host1x->devices_lock held.
|
||||
*/
|
||||
static void host1x_device_del(struct host1x *host1x,
|
||||
struct host1x_device *device)
|
||||
static int host1x_device_remove(struct device *dev)
|
||||
{
|
||||
struct host1x_driver *driver = to_host1x_driver(dev->driver);
|
||||
struct host1x_device *device = to_host1x_device(dev);
|
||||
|
||||
if (driver->remove)
|
||||
return driver->remove(device);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void host1x_device_shutdown(struct device *dev)
|
||||
{
|
||||
struct host1x_driver *driver = to_host1x_driver(dev->driver);
|
||||
struct host1x_device *device = to_host1x_device(dev);
|
||||
|
||||
if (driver->shutdown)
|
||||
driver->shutdown(device);
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops host1x_device_pm_ops = {
|
||||
.suspend = pm_generic_suspend,
|
||||
.resume = pm_generic_resume,
|
||||
.freeze = pm_generic_freeze,
|
||||
.thaw = pm_generic_thaw,
|
||||
.poweroff = pm_generic_poweroff,
|
||||
.restore = pm_generic_restore,
|
||||
};
|
||||
|
||||
struct bus_type host1x_bus_type = {
|
||||
.name = "host1x",
|
||||
.match = host1x_device_match,
|
||||
.probe = host1x_device_probe,
|
||||
.remove = host1x_device_remove,
|
||||
.shutdown = host1x_device_shutdown,
|
||||
.pm = &host1x_device_pm_ops,
|
||||
};
|
||||
|
||||
static void __host1x_device_del(struct host1x_device *device)
|
||||
{
|
||||
struct host1x_subdev *subdev, *sd;
|
||||
struct host1x_client *client, *cl;
|
||||
@@ -391,7 +359,84 @@ static void host1x_device_del(struct host1x *host1x,
|
||||
|
||||
/* finally remove the device */
|
||||
list_del_init(&device->list);
|
||||
device_unregister(&device->dev);
|
||||
}
|
||||
|
||||
static void host1x_device_release(struct device *dev)
|
||||
{
|
||||
struct host1x_device *device = to_host1x_device(dev);
|
||||
|
||||
__host1x_device_del(device);
|
||||
kfree(device);
|
||||
}
|
||||
|
||||
static int host1x_device_add(struct host1x *host1x,
|
||||
struct host1x_driver *driver)
|
||||
{
|
||||
struct host1x_client *client, *tmp;
|
||||
struct host1x_subdev *subdev;
|
||||
struct host1x_device *device;
|
||||
int err;
|
||||
|
||||
device = kzalloc(sizeof(*device), GFP_KERNEL);
|
||||
if (!device)
|
||||
return -ENOMEM;
|
||||
|
||||
device_initialize(&device->dev);
|
||||
|
||||
mutex_init(&device->subdevs_lock);
|
||||
INIT_LIST_HEAD(&device->subdevs);
|
||||
INIT_LIST_HEAD(&device->active);
|
||||
mutex_init(&device->clients_lock);
|
||||
INIT_LIST_HEAD(&device->clients);
|
||||
INIT_LIST_HEAD(&device->list);
|
||||
device->driver = driver;
|
||||
|
||||
device->dev.coherent_dma_mask = host1x->dev->coherent_dma_mask;
|
||||
device->dev.dma_mask = &device->dev.coherent_dma_mask;
|
||||
dev_set_name(&device->dev, "%s", driver->driver.name);
|
||||
device->dev.release = host1x_device_release;
|
||||
device->dev.bus = &host1x_bus_type;
|
||||
device->dev.parent = host1x->dev;
|
||||
|
||||
err = host1x_device_parse_dt(device, driver);
|
||||
if (err < 0) {
|
||||
kfree(device);
|
||||
return err;
|
||||
}
|
||||
|
||||
list_add_tail(&device->list, &host1x->devices);
|
||||
|
||||
mutex_lock(&clients_lock);
|
||||
|
||||
list_for_each_entry_safe(client, tmp, &clients, list) {
|
||||
list_for_each_entry(subdev, &device->subdevs, list) {
|
||||
if (subdev->np == client->dev->of_node) {
|
||||
host1x_subdev_register(device, subdev, client);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mutex_unlock(&clients_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Removes a device by first unregistering any subdevices and then removing
|
||||
* itself from the list of devices.
|
||||
*
|
||||
* This function must be called with the host1x->devices_lock held.
|
||||
*/
|
||||
static void host1x_device_del(struct host1x *host1x,
|
||||
struct host1x_device *device)
|
||||
{
|
||||
if (device->registered) {
|
||||
device->registered = false;
|
||||
device_del(&device->dev);
|
||||
}
|
||||
|
||||
put_device(&device->dev);
|
||||
}
|
||||
|
||||
static void host1x_attach_driver(struct host1x *host1x,
|
||||
@@ -409,11 +454,11 @@ static void host1x_attach_driver(struct host1x *host1x,
|
||||
}
|
||||
}
|
||||
|
||||
mutex_unlock(&host1x->devices_lock);
|
||||
|
||||
err = host1x_device_add(host1x, driver);
|
||||
if (err < 0)
|
||||
dev_err(host1x->dev, "failed to allocate device: %d\n", err);
|
||||
|
||||
mutex_unlock(&host1x->devices_lock);
|
||||
}
|
||||
|
||||
static void host1x_detach_driver(struct host1x *host1x,
|
||||
@@ -466,7 +511,8 @@ int host1x_unregister(struct host1x *host1x)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int host1x_driver_register(struct host1x_driver *driver)
|
||||
int host1x_driver_register_full(struct host1x_driver *driver,
|
||||
struct module *owner)
|
||||
{
|
||||
struct host1x *host1x;
|
||||
|
||||
@@ -483,9 +529,12 @@ int host1x_driver_register(struct host1x_driver *driver)
|
||||
|
||||
mutex_unlock(&devices_lock);
|
||||
|
||||
return 0;
|
||||
driver->driver.bus = &host1x_bus_type;
|
||||
driver->driver.owner = owner;
|
||||
|
||||
return driver_register(&driver->driver);
|
||||
}
|
||||
EXPORT_SYMBOL(host1x_driver_register);
|
||||
EXPORT_SYMBOL(host1x_driver_register_full);
|
||||
|
||||
void host1x_driver_unregister(struct host1x_driver *driver)
|
||||
{
|
||||
|
||||
@@ -18,10 +18,10 @@
|
||||
#ifndef HOST1X_BUS_H
|
||||
#define HOST1X_BUS_H
|
||||
|
||||
struct bus_type;
|
||||
struct host1x;
|
||||
|
||||
int host1x_bus_init(void);
|
||||
void host1x_bus_exit(void);
|
||||
extern struct bus_type host1x_bus_type;
|
||||
|
||||
int host1x_register(struct host1x *host1x);
|
||||
int host1x_unregister(struct host1x *host1x);
|
||||
|
||||
@@ -216,7 +216,7 @@ static int __init tegra_host1x_init(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = host1x_bus_init();
|
||||
err = bus_register(&host1x_bus_type);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
@@ -233,7 +233,7 @@ static int __init tegra_host1x_init(void)
|
||||
unregister_host1x:
|
||||
platform_driver_unregister(&tegra_host1x_driver);
|
||||
unregister_bus:
|
||||
host1x_bus_exit();
|
||||
bus_unregister(&host1x_bus_type);
|
||||
return err;
|
||||
}
|
||||
module_init(tegra_host1x_init);
|
||||
@@ -242,7 +242,7 @@ static void __exit tegra_host1x_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&tegra_mipi_driver);
|
||||
platform_driver_unregister(&tegra_host1x_driver);
|
||||
host1x_bus_exit();
|
||||
bus_unregister(&host1x_bus_type);
|
||||
}
|
||||
module_exit(tegra_host1x_exit);
|
||||
|
||||
|
||||
@@ -127,4 +127,41 @@ void drm_atomic_helper_connector_destroy_state(struct drm_connector *connector,
|
||||
#define drm_atomic_crtc_state_for_each_plane(plane, crtc_state) \
|
||||
drm_for_each_plane_mask(plane, (crtc_state)->state->dev, (crtc_state)->plane_mask)
|
||||
|
||||
/*
|
||||
* drm_atomic_plane_disabling - check whether a plane is being disabled
|
||||
* @plane: plane object
|
||||
* @old_state: previous atomic state
|
||||
*
|
||||
* Checks the atomic state of a plane to determine whether it's being disabled
|
||||
* or not. This also WARNs if it detects an invalid state (both CRTC and FB
|
||||
* need to either both be NULL or both be non-NULL).
|
||||
*
|
||||
* RETURNS:
|
||||
* True if the plane is being disabled, false otherwise.
|
||||
*/
|
||||
static inline bool
|
||||
drm_atomic_plane_disabling(struct drm_plane *plane,
|
||||
struct drm_plane_state *old_state)
|
||||
{
|
||||
/*
|
||||
* When disabling a plane, CRTC and FB should always be NULL together.
|
||||
* Anything else should be considered a bug in the atomic core, so we
|
||||
* gently warn about it.
|
||||
*/
|
||||
WARN_ON((plane->state->crtc == NULL && plane->state->fb != NULL) ||
|
||||
(plane->state->crtc != NULL && plane->state->fb == NULL));
|
||||
|
||||
/*
|
||||
* When using the transitional helpers, old_state may be NULL. If so,
|
||||
* we know nothing about the current state and have to assume that it
|
||||
* might be enabled.
|
||||
*
|
||||
* When using the atomic helpers, old_state won't be NULL. Therefore
|
||||
* this check assumes that either the driver will have reconstructed
|
||||
* the correct state in ->reset() or that the driver will have taken
|
||||
* appropriate measures to disable all planes.
|
||||
*/
|
||||
return (!old_state || old_state->crtc) && !plane->state->crtc;
|
||||
}
|
||||
|
||||
#endif /* DRM_ATOMIC_HELPER_H_ */
|
||||
|
||||
@@ -115,6 +115,7 @@ struct drm_crtc_helper_funcs {
|
||||
* @get_crtc: return CRTC that the encoder is currently attached to
|
||||
* @detect: connection status detection
|
||||
* @disable: disable encoder when not in use (overrides DPMS off)
|
||||
* @atomic_check: check for validity of an atomic update
|
||||
*
|
||||
* The helper operations are called by the mid-layer CRTC helper.
|
||||
*/
|
||||
@@ -137,6 +138,11 @@ struct drm_encoder_helper_funcs {
|
||||
struct drm_connector *connector);
|
||||
/* disable encoder when not in use - more explicit than dpms off */
|
||||
void (*disable)(struct drm_encoder *encoder);
|
||||
|
||||
/* atomic helpers */
|
||||
int (*atomic_check)(struct drm_encoder *encoder,
|
||||
struct drm_crtc_state *crtc_state,
|
||||
struct drm_connector_state *conn_state);
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -52,7 +52,8 @@ extern int drm_crtc_init(struct drm_device *dev,
|
||||
* @prepare_fb: prepare a framebuffer for use by the plane
|
||||
* @cleanup_fb: cleanup a framebuffer when it's no longer used by the plane
|
||||
* @atomic_check: check that a given atomic state is valid and can be applied
|
||||
* @atomic_update: apply an atomic state to the plane
|
||||
* @atomic_update: apply an atomic state to the plane (mandatory)
|
||||
* @atomic_disable: disable the plane
|
||||
*
|
||||
* The helper operations are called by the mid-layer CRTC helper.
|
||||
*/
|
||||
@@ -66,6 +67,8 @@ struct drm_plane_helper_funcs {
|
||||
struct drm_plane_state *state);
|
||||
void (*atomic_update)(struct drm_plane *plane,
|
||||
struct drm_plane_state *old_state);
|
||||
void (*atomic_disable)(struct drm_plane *plane,
|
||||
struct drm_plane_state *old_state);
|
||||
};
|
||||
|
||||
static inline void drm_plane_helper_add(struct drm_plane *plane,
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user