diff options
author | Dave Airlie <airlied@redhat.com> | 2017-03-23 01:53:41 +0300 |
---|---|---|
committer | Dave Airlie <airlied@redhat.com> | 2017-03-23 01:53:41 +0300 |
commit | edd849e5448c4f6ddc04a5fa1ac5479176660c27 (patch) | |
tree | 2a055fc27ab9263ba34bef89717b5028f49b0bc2 /drivers | |
parent | be5df20a34d78d986ddfb6e0fc87e4fa4d05268b (diff) | |
parent | 62c58af32c935a98a1e8d8ceb39a3a47b36fbbcd (diff) | |
download | linux-edd849e5448c4f6ddc04a5fa1ac5479176660c27.tar.xz |
Merge tag 'drm-misc-next-2017-03-21' of git://anongit.freedesktop.org/git/drm-misc into drm-next
drm-misc for 4.12, 2nd attempt this week:
- topic branch from Jon Corbet for the new graph kerneldoc support
- lots of graphs for kms/atomic things using the above
- some vblank query tuning from Chris
- gem/cma_fops macros
- moar docs
Driver stuff:
- vc4 hdmi audio, yay (Eric)
- dw-hdmi polish from a bunch of people
- some rockchip dp updates that didn't make last week (Chris Zhong)
- misc bridge&driver updates
* tag 'drm-misc-next-2017-03-21' of git://anongit.freedesktop.org/git/drm-misc: (37 commits)
drm/edid: detect SCDC support in HF-VSDB
drm/edid: detect SCDC support in HF-VSDB
drm/edid: check for HF-VSDB block
drm: Add SCDC helpers
drm: bridge: dw-hdmi: add HDMI vendor specific infoframe config
drm/bridge: dw_hdmi: support i2c extended read mode
drm/msm: add stubs for msm_{perf,rd}_debugfs_cleanup
drm: bochs: Don't remove uninitialized fbdev framebuffer
drm: vc4: remove redundant check of plane being non-null
drm/vc4: use platform_register_drivers
dma-fence: add dma_fence_match_context helper
drm/vc4: Add HDMI audio support
dt-bindings: Document the dmas and dma-names properties for VC4 HDMI
drm/atmel-hlcdc: Fix suspend/resume implementation
drm: Skip the waitqueue setup for vblank queries
drm: Defer disabling the vblank IRQ until the next interrupt (for instant-off)
drm/doc: atomic overview, with graph
drm/doc: diagram for mode objects and properties
drm/doc: Consistent kerneldoc include order
drm/doc: Add KMS overview graphs
...
Diffstat (limited to 'drivers')
42 files changed, 1163 insertions, 341 deletions
diff --git a/drivers/dma-buf/dma-fence-array.c b/drivers/dma-buf/dma-fence-array.c index 67eb7c8fb88c..0350829ba62e 100644 --- a/drivers/dma-buf/dma-fence-array.c +++ b/drivers/dma-buf/dma-fence-array.c @@ -144,3 +144,29 @@ struct dma_fence_array *dma_fence_array_create(int num_fences, return array; } EXPORT_SYMBOL(dma_fence_array_create); + +/** + * dma_fence_match_context - Check if all fences are from the given context + * @fence: [in] fence or fence array + * @context: [in] fence context to check all fences against + * + * Checks the provided fence or, for a fence array, all fences in the array + * against the given context. Returns false if any fence is from a different + * context. + */ +bool dma_fence_match_context(struct dma_fence *fence, u64 context) +{ + struct dma_fence_array *array = to_dma_fence_array(fence); + unsigned i; + + if (!dma_fence_is_array(fence)) + return fence->context == context; + + for (i = 0; i < array->num_fences; i++) { + if (array->fences[i]->context != context) + return false; + } + + return true; +} +EXPORT_SYMBOL(dma_fence_match_context); diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index 59aae43005ee..59f0f9b696eb 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -31,7 +31,8 @@ drm-$(CONFIG_DEBUG_FS) += drm_debugfs.o drm_debugfs_crc.o drm_kms_helper-y := drm_crtc_helper.o drm_dp_helper.o drm_probe_helper.o \ drm_plane_helper.o drm_dp_mst_topology.o drm_atomic_helper.o \ drm_kms_helper_common.o drm_dp_dual_mode_helper.o \ - drm_simple_kms_helper.o drm_modeset_helper.o + drm_simple_kms_helper.o drm_modeset_helper.o \ + drm_scdc_helper.o drm_kms_helper-$(CONFIG_DRM_LOAD_EDID_FIRMWARE) += drm_edid_load.o drm_kms_helper-$(CONFIG_DRM_FBDEV_EMULATION) += drm_fb_helper.o diff --git a/drivers/gpu/drm/arm/hdlcd_drv.c b/drivers/gpu/drm/arm/hdlcd_drv.c index 4840dc277339..0e74880b5e94 100644 --- a/drivers/gpu/drm/arm/hdlcd_drv.c +++ b/drivers/gpu/drm/arm/hdlcd_drv.c @@ -239,17 +239,7 @@ static int hdlcd_debugfs_init(struct drm_minor *minor) } #endif -static const struct file_operations fops = { - .owner = THIS_MODULE, - .open = drm_open, - .release = drm_release, - .unlocked_ioctl = drm_ioctl, - .compat_ioctl = drm_compat_ioctl, - .poll = drm_poll, - .read = drm_read, - .llseek = noop_llseek, - .mmap = drm_gem_cma_mmap, -}; +DEFINE_DRM_GEM_CMA_FOPS(fops); static struct drm_driver hdlcd_driver = { .driver_features = DRIVER_HAVE_IRQ | DRIVER_GEM | diff --git a/drivers/gpu/drm/arm/malidp_drv.c b/drivers/gpu/drm/arm/malidp_drv.c index a9608a2e5a29..ea2546f766c2 100644 --- a/drivers/gpu/drm/arm/malidp_drv.c +++ b/drivers/gpu/drm/arm/malidp_drv.c @@ -178,17 +178,7 @@ static void malidp_lastclose(struct drm_device *drm) drm_fbdev_cma_restore_mode(malidp->fbdev); } -static const struct file_operations fops = { - .owner = THIS_MODULE, - .open = drm_open, - .release = drm_release, - .unlocked_ioctl = drm_ioctl, - .compat_ioctl = drm_compat_ioctl, - .poll = drm_poll, - .read = drm_read, - .llseek = noop_llseek, - .mmap = drm_gem_cma_mmap, -}; +DEFINE_DRM_GEM_CMA_FOPS(fops); static struct drm_driver malidp_driver = { .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC | diff --git a/drivers/gpu/drm/armada/armada_drv.c b/drivers/gpu/drm/armada/armada_drv.c index 408e00b64cf6..e618fab7f519 100644 --- a/drivers/gpu/drm/armada/armada_drv.c +++ b/drivers/gpu/drm/armada/armada_drv.c @@ -60,16 +60,7 @@ static void armada_drm_lastclose(struct drm_device *dev) armada_fbdev_lastclose(dev); } -static const struct file_operations armada_drm_fops = { - .owner = THIS_MODULE, - .llseek = no_llseek, - .read = drm_read, - .poll = drm_poll, - .unlocked_ioctl = drm_ioctl, - .mmap = drm_gem_mmap, - .open = drm_open, - .release = drm_release, -}; +DEFINE_DRM_GEM_FOPS(armada_drm_fops); static struct drm_driver armada_drm_driver = { .lastclose = armada_drm_lastclose, diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c index 6b50fb706c0e..53bfa56ca47a 100644 --- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c +++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c @@ -55,14 +55,12 @@ drm_crtc_state_to_atmel_hlcdc_crtc_state(struct drm_crtc_state *state) * @hlcdc: pointer to the atmel_hlcdc structure provided by the MFD device * @event: pointer to the current page flip event * @id: CRTC id (returned by drm_crtc_index) - * @enabled: CRTC state */ struct atmel_hlcdc_crtc { struct drm_crtc base; struct atmel_hlcdc_dc *dc; struct drm_pending_vblank_event *event; int id; - bool enabled; }; static inline struct atmel_hlcdc_crtc * @@ -158,9 +156,6 @@ static void atmel_hlcdc_crtc_disable(struct drm_crtc *c) struct regmap *regmap = crtc->dc->hlcdc->regmap; unsigned int status; - if (!crtc->enabled) - return; - drm_crtc_vblank_off(c); pm_runtime_get_sync(dev->dev); @@ -186,8 +181,6 @@ static void atmel_hlcdc_crtc_disable(struct drm_crtc *c) pm_runtime_allow(dev->dev); pm_runtime_put_sync(dev->dev); - - crtc->enabled = false; } static void atmel_hlcdc_crtc_enable(struct drm_crtc *c) @@ -197,9 +190,6 @@ static void atmel_hlcdc_crtc_enable(struct drm_crtc *c) struct regmap *regmap = crtc->dc->hlcdc->regmap; unsigned int status; - if (crtc->enabled) - return; - pm_runtime_get_sync(dev->dev); pm_runtime_forbid(dev->dev); @@ -226,29 +216,6 @@ static void atmel_hlcdc_crtc_enable(struct drm_crtc *c) pm_runtime_put_sync(dev->dev); drm_crtc_vblank_on(c); - - crtc->enabled = true; -} - -void atmel_hlcdc_crtc_suspend(struct drm_crtc *c) -{ - struct atmel_hlcdc_crtc *crtc = drm_crtc_to_atmel_hlcdc_crtc(c); - - if (crtc->enabled) { - atmel_hlcdc_crtc_disable(c); - /* save enable state for resume */ - crtc->enabled = true; - } -} - -void atmel_hlcdc_crtc_resume(struct drm_crtc *c) -{ - struct atmel_hlcdc_crtc *crtc = drm_crtc_to_atmel_hlcdc_crtc(c); - - if (crtc->enabled) { - crtc->enabled = false; - atmel_hlcdc_crtc_enable(c); - } } #define ATMEL_HLCDC_RGB444_OUTPUT BIT(0) diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c index 970bd87d7cce..f4a3065f7f51 100644 --- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c +++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c @@ -724,17 +724,7 @@ static void atmel_hlcdc_dc_irq_uninstall(struct drm_device *dev) regmap_read(dc->hlcdc->regmap, ATMEL_HLCDC_ISR, &isr); } -static const struct file_operations fops = { - .owner = THIS_MODULE, - .open = drm_open, - .release = drm_release, - .unlocked_ioctl = drm_ioctl, - .compat_ioctl = drm_compat_ioctl, - .poll = drm_poll, - .read = drm_read, - .llseek = no_llseek, - .mmap = drm_gem_cma_mmap, -}; +DEFINE_DRM_GEM_CMA_FOPS(fops); static struct drm_driver atmel_hlcdc_dc_driver = { .driver_features = DRIVER_HAVE_IRQ | DRIVER_GEM | @@ -810,31 +800,32 @@ static int atmel_hlcdc_dc_drm_remove(struct platform_device *pdev) static int atmel_hlcdc_dc_drm_suspend(struct device *dev) { struct drm_device *drm_dev = dev_get_drvdata(dev); - struct drm_crtc *crtc; + struct atmel_hlcdc_dc *dc = drm_dev->dev_private; + struct regmap *regmap = dc->hlcdc->regmap; + struct drm_atomic_state *state; + + state = drm_atomic_helper_suspend(drm_dev); + if (IS_ERR(state)) + return PTR_ERR(state); - if (pm_runtime_suspended(dev)) - return 0; + dc->suspend.state = state; + + regmap_read(regmap, ATMEL_HLCDC_IMR, &dc->suspend.imr); + regmap_write(regmap, ATMEL_HLCDC_IDR, dc->suspend.imr); + clk_disable_unprepare(dc->hlcdc->periph_clk); - drm_modeset_lock_all(drm_dev); - list_for_each_entry(crtc, &drm_dev->mode_config.crtc_list, head) - atmel_hlcdc_crtc_suspend(crtc); - drm_modeset_unlock_all(drm_dev); return 0; } static int atmel_hlcdc_dc_drm_resume(struct device *dev) { struct drm_device *drm_dev = dev_get_drvdata(dev); - struct drm_crtc *crtc; + struct atmel_hlcdc_dc *dc = drm_dev->dev_private; - if (pm_runtime_suspended(dev)) - return 0; + clk_prepare_enable(dc->hlcdc->periph_clk); + regmap_write(dc->hlcdc->regmap, ATMEL_HLCDC_IER, dc->suspend.imr); - drm_modeset_lock_all(drm_dev); - list_for_each_entry(crtc, &drm_dev->mode_config.crtc_list, head) - atmel_hlcdc_crtc_resume(crtc); - drm_modeset_unlock_all(drm_dev); - return 0; + return drm_atomic_helper_resume(drm_dev, dc->suspend.state); } #endif diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h index da7f25a59be5..433641b6e23b 100644 --- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h +++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h @@ -358,6 +358,7 @@ struct atmel_hlcdc_plane_properties { * @planes: instantiated planes * @layers: active HLCDC layers * @wq: display controller workqueue + * @suspend: used to store the HLCDC state when entering suspend * @commit: used for async commit handling */ struct atmel_hlcdc_dc { @@ -369,6 +370,10 @@ struct atmel_hlcdc_dc { struct atmel_hlcdc_layer *layers[ATMEL_HLCDC_MAX_LAYERS]; struct workqueue_struct *wq; struct { + u32 imr; + struct drm_atomic_state *state; + } suspend; + struct { wait_queue_head_t wait; bool pending; } commit; @@ -428,9 +433,6 @@ int atmel_hlcdc_plane_prepare_ahb_routing(struct drm_crtc_state *c_state); void atmel_hlcdc_crtc_irq(struct drm_crtc *c); -void atmel_hlcdc_crtc_suspend(struct drm_crtc *crtc); -void atmel_hlcdc_crtc_resume(struct drm_crtc *crtc); - int atmel_hlcdc_crtc_create(struct drm_device *dev); int atmel_hlcdc_create_outputs(struct drm_device *dev); diff --git a/drivers/gpu/drm/bochs/bochs_fbdev.c b/drivers/gpu/drm/bochs/bochs_fbdev.c index a11debaad626..471bd588550b 100644 --- a/drivers/gpu/drm/bochs/bochs_fbdev.c +++ b/drivers/gpu/drm/bochs/bochs_fbdev.c @@ -132,6 +132,7 @@ static int bochsfb_create(struct drm_fb_helper *helper, info->fix.smem_start = 0; info->fix.smem_len = size; + bochs->fb.initialized = true; return 0; } @@ -148,7 +149,6 @@ static int bochs_fbdev_destroy(struct bochs_device *bochs) gfb->obj = NULL; } - drm_fb_helper_fini(&bochs->fb.helper); drm_framebuffer_unregister_private(&gfb->base); drm_framebuffer_cleanup(&gfb->base); @@ -180,7 +180,6 @@ int bochs_fbdev_init(struct bochs_device *bochs) if (ret) goto fini; - bochs->fb.initialized = true; return 0; fini: @@ -190,9 +189,9 @@ fini: void bochs_fbdev_fini(struct bochs_device *bochs) { - if (!bochs->fb.initialized) - return; + if (bochs->fb.initialized) + bochs_fbdev_destroy(bochs); - bochs_fbdev_destroy(bochs); + drm_fb_helper_fini(&bochs->fb.helper); bochs->fb.initialized = false; } diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c index 026a0dce7661..af93f7a20697 100644 --- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c @@ -33,6 +33,7 @@ #include "dw-hdmi.h" #include "dw-hdmi-audio.h" +#define DDC_SEGMENT_ADDR 0x30 #define HDMI_EDID_LEN 512 #define RGB 0 @@ -112,6 +113,7 @@ struct dw_hdmi_i2c { u8 slave_reg; bool is_regaddr; + bool is_segment; }; struct dw_hdmi_phy_data { @@ -247,8 +249,12 @@ static int dw_hdmi_i2c_read(struct dw_hdmi *hdmi, reinit_completion(&i2c->cmp); hdmi_writeb(hdmi, i2c->slave_reg++, HDMI_I2CM_ADDRESS); - hdmi_writeb(hdmi, HDMI_I2CM_OPERATION_READ, - HDMI_I2CM_OPERATION); + if (i2c->is_segment) + hdmi_writeb(hdmi, HDMI_I2CM_OPERATION_READ_EXT, + HDMI_I2CM_OPERATION); + else + hdmi_writeb(hdmi, HDMI_I2CM_OPERATION_READ, + HDMI_I2CM_OPERATION); stat = wait_for_completion_timeout(&i2c->cmp, HZ / 10); if (!stat) @@ -260,6 +266,7 @@ static int dw_hdmi_i2c_read(struct dw_hdmi *hdmi, *buf++ = hdmi_readb(hdmi, HDMI_I2CM_DATAI); } + i2c->is_segment = false; return 0; } @@ -309,12 +316,6 @@ static int dw_hdmi_i2c_xfer(struct i2c_adapter *adap, dev_dbg(hdmi->dev, "xfer: num: %d, addr: %#x\n", num, addr); for (i = 0; i < num; i++) { - if (msgs[i].addr != addr) { - dev_warn(hdmi->dev, - "unsupported transfer, changed slave address\n"); - return -EOPNOTSUPP; - } - if (msgs[i].len == 0) { dev_dbg(hdmi->dev, "unsupported transfer %d/%d, no data\n", @@ -334,15 +335,24 @@ static int dw_hdmi_i2c_xfer(struct i2c_adapter *adap, /* Set slave device register address on transfer */ i2c->is_regaddr = false; + /* Set segment pointer for I2C extended read mode operation */ + i2c->is_segment = false; + for (i = 0; i < num; i++) { dev_dbg(hdmi->dev, "xfer: num: %d/%d, len: %d, flags: %#x\n", i + 1, num, msgs[i].len, msgs[i].flags); - - if (msgs[i].flags & I2C_M_RD) - ret = dw_hdmi_i2c_read(hdmi, msgs[i].buf, msgs[i].len); - else - ret = dw_hdmi_i2c_write(hdmi, msgs[i].buf, msgs[i].len); - + if (msgs[i].addr == DDC_SEGMENT_ADDR && msgs[i].len == 1) { + i2c->is_segment = true; + hdmi_writeb(hdmi, DDC_SEGMENT_ADDR, HDMI_I2CM_SEGADDR); + hdmi_writeb(hdmi, *msgs[i].buf, HDMI_I2CM_SEGPTR); + } else { + if (msgs[i].flags & I2C_M_RD) + ret = dw_hdmi_i2c_read(hdmi, msgs[i].buf, + msgs[i].len); + else + ret = dw_hdmi_i2c_write(hdmi, msgs[i].buf, + msgs[i].len); + } if (ret < 0) break; } @@ -1230,6 +1240,58 @@ static void hdmi_config_AVI(struct dw_hdmi *hdmi, struct drm_display_mode *mode) hdmi_writeb(hdmi, (frame.right_bar >> 8) & 0xff, HDMI_FC_AVISRB1); } +static void hdmi_config_vendor_specific_infoframe(struct dw_hdmi *hdmi, + struct drm_display_mode *mode) +{ + struct hdmi_vendor_infoframe frame; + u8 buffer[10]; + ssize_t err; + + err = drm_hdmi_vendor_infoframe_from_display_mode(&frame, mode); + if (err < 0) + /* + * Going into that statement does not means vendor infoframe + * fails. It just informed us that vendor infoframe is not + * needed for the selected mode. Only 4k or stereoscopic 3D + * mode requires vendor infoframe. So just simply return. + */ + return; + + err = hdmi_vendor_infoframe_pack(&frame, buffer, sizeof(buffer)); + if (err < 0) { + dev_err(hdmi->dev, "Failed to pack vendor infoframe: %zd\n", + err); + return; + } + hdmi_mask_writeb(hdmi, 0, HDMI_FC_DATAUTO0, HDMI_FC_DATAUTO0_VSD_OFFSET, + HDMI_FC_DATAUTO0_VSD_MASK); + + /* Set the length of HDMI vendor specific InfoFrame payload */ + hdmi_writeb(hdmi, buffer[2], HDMI_FC_VSDSIZE); + + /* Set 24bit IEEE Registration Identifier */ + hdmi_writeb(hdmi, buffer[4], HDMI_FC_VSDIEEEID0); + hdmi_writeb(hdmi, buffer[5], HDMI_FC_VSDIEEEID1); + hdmi_writeb(hdmi, buffer[6], HDMI_FC_VSDIEEEID2); + + /* Set HDMI_Video_Format and HDMI_VIC/3D_Structure */ + hdmi_writeb(hdmi, buffer[7], HDMI_FC_VSDPAYLOAD0); + hdmi_writeb(hdmi, buffer[8], HDMI_FC_VSDPAYLOAD1); + + if (frame.s3d_struct >= HDMI_3D_STRUCTURE_SIDE_BY_SIDE_HALF) + hdmi_writeb(hdmi, buffer[9], HDMI_FC_VSDPAYLOAD2); + + /* Packet frame interpolation */ + hdmi_writeb(hdmi, 1, HDMI_FC_DATAUTO1); + + /* Auto packets per frame and line spacing */ + hdmi_writeb(hdmi, 0x11, HDMI_FC_DATAUTO2); + + /* Configures the Frame Composer On RDRB mode */ + hdmi_mask_writeb(hdmi, 1, HDMI_FC_DATAUTO0, HDMI_FC_DATAUTO0_VSD_OFFSET, + HDMI_FC_DATAUTO0_VSD_MASK); +} + static void hdmi_av_composer(struct dw_hdmi *hdmi, const struct drm_display_mode *mode) { @@ -1479,6 +1541,7 @@ static int dw_hdmi_setup(struct dw_hdmi *hdmi, struct drm_display_mode *mode) /* HDMI Initialization Step F - Configure AVI InfoFrame */ hdmi_config_AVI(hdmi, mode); + hdmi_config_vendor_specific_infoframe(hdmi, mode); } else { dev_dbg(hdmi->dev, "%s DVI mode\n", __func__); } diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.h b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.h index 325b0b8ae639..c59f87e1483e 100644 --- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.h +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.h @@ -854,6 +854,10 @@ enum { HDMI_FC_DBGFORCE_FORCEAUDIO = 0x10, HDMI_FC_DBGFORCE_FORCEVIDEO = 0x1, +/* FC_DATAUTO0 field values */ + HDMI_FC_DATAUTO0_VSD_MASK = 0x08, + HDMI_FC_DATAUTO0_VSD_OFFSET = 3, + /* PHY_CONF0 field values */ HDMI_PHY_CONF0_PDZ_MASK = 0x80, HDMI_PHY_CONF0_PDZ_OFFSET = 7, diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index 171d7a02ace0..99144f879a4f 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -37,6 +37,7 @@ #include <drm/drm_edid.h> #include <drm/drm_encoder.h> #include <drm/drm_displayid.h> +#include <drm/drm_scdc_helper.h> #include "drm_crtc_internal.h" @@ -3248,6 +3249,21 @@ static bool cea_db_is_hdmi_vsdb(const u8 *db) return hdmi_id == HDMI_IEEE_OUI; } +static bool cea_db_is_hdmi_forum_vsdb(const u8 *db) +{ + unsigned int oui; + + if (cea_db_tag(db) != VENDOR_BLOCK) + return false; + + if (cea_db_payload_len(db) < 7) + return false; + + oui = db[3] << 16 | db[2] << 8 | db[1]; + + return oui == HDMI_FORUM_IEEE_OUI; +} + #define for_each_cea_db(cea, i, start, end) \ for ((i) = (start); (i) < (end) && (i) + cea_db_payload_len(&(cea)[(i)]) < (end); (i) += cea_db_payload_len(&(cea)[(i)]) + 1) @@ -3799,6 +3815,48 @@ drm_default_rgb_quant_range(const struct drm_display_mode *mode) } EXPORT_SYMBOL(drm_default_rgb_quant_range); +static void drm_parse_hdmi_forum_vsdb(struct drm_connector *connector, + const u8 *hf_vsdb) +{ + struct drm_display_info *display = &connector->display_info; + struct drm_hdmi_info *hdmi = &display->hdmi; + + if (hf_vsdb[6] & 0x80) { + hdmi->scdc.supported = true; + if (hf_vsdb[6] & 0x40) + hdmi->scdc.read_request = true; + } + + /* + * All HDMI 2.0 monitors must support scrambling at rates > 340 MHz. + * And as per the spec, three factors confirm this: + * * Availability of a HF-VSDB block in EDID (check) + * * Non zero Max_TMDS_Char_Rate filed in HF-VSDB (let's check) + * * SCDC support available (let's check) + * Lets check it out. + */ + + if (hf_vsdb[5]) { + /* max clock is 5000 KHz times block value */ + u32 max_tmds_clock = hf_vsdb[5] * 5000; + struct drm_scdc *scdc = &hdmi->scdc; + + if (max_tmds_clock > 340000) { + display->max_tmds_clock = max_tmds_clock; + DRM_DEBUG_KMS("HF-VSDB: max TMDS clock %d kHz\n", + display->max_tmds_clock); + } + + if (scdc->supported) { + scdc->scrambling.supported = true; + + /* Few sinks support scrambling for cloks < 340M */ + if ((hf_vsdb[6] & 0x8)) + scdc->scrambling.low_rates = true; + } + } +} + static void drm_parse_hdmi_deep_color_info(struct drm_connector *connector, const u8 *hdmi) { @@ -3913,6 +3971,8 @@ static void drm_parse_cea_ext(struct drm_connector *connector, if (cea_db_is_hdmi_vsdb(db)) drm_parse_hdmi_vsdb_video(connector, db); + if (cea_db_is_hdmi_forum_vsdb(db)) + drm_parse_hdmi_forum_vsdb(connector, db); } } diff --git a/drivers/gpu/drm/drm_file.c b/drivers/gpu/drm/drm_file.c index d9e63d73d3ec..3783b659cd38 100644 --- a/drivers/gpu/drm/drm_file.c +++ b/drivers/gpu/drm/drm_file.c @@ -50,12 +50,14 @@ DEFINE_MUTEX(drm_global_mutex); * * Drivers must define the file operations structure that forms the DRM * userspace API entry point, even though most of those operations are - * implemented in the DRM core. The mandatory functions are drm_open(), + * implemented in the DRM core. The resulting &struct file_operations must be + * stored in the &drm_driver.fops field. The mandatory functions are drm_open(), * drm_read(), drm_ioctl() and drm_compat_ioctl() if CONFIG_COMPAT is enabled - * (note that drm_compat_ioctl will be NULL if CONFIG_COMPAT=n). Drivers which - * implement private ioctls that require 32/64 bit compatibility support must - * provide their own .compat_ioctl() handler that processes private ioctls and - * calls drm_compat_ioctl() for core ioctls. + * Note that drm_compat_ioctl will be NULL if CONFIG_COMPAT=n, so there's no + * need to sprinkle #ifdef into the code. Drivers which implement private ioctls + * that require 32/64 bit compatibility support must provide their own + * &file_operations.compat_ioctl handler that processes private ioctls and calls + * drm_compat_ioctl() for core ioctls. * * In addition drm_read() and drm_poll() provide support for DRM events. DRM * events are a generic and extensible means to send asynchronous events to @@ -63,10 +65,14 @@ DEFINE_MUTEX(drm_global_mutex); * page flip completions by the KMS API. But drivers can also use it for their * own needs, e.g. to signal completion of rendering. * + * For the driver-side event interface see drm_event_reserve_init() and + * drm_send_event() as the main starting points. + * * The memory mapping implementation will vary depending on how the driver * manages memory. Legacy drivers will use the deprecated drm_legacy_mmap() * function, modern drivers should use one of the provided memory-manager - * specific implementations. For GEM-based drivers this is drm_gem_mmap(). + * specific implementations. For GEM-based drivers this is drm_gem_mmap(), and + * for drivers which use the CMA GEM helpers it's drm_gem_cma_mmap(). * * No other file operations are supported by the DRM userspace API. Overall the * following is an example #file_operations structure:: @@ -82,6 +88,10 @@ DEFINE_MUTEX(drm_global_mutex); * .llseek = no_llseek, * .mmap = drm_gem_mmap, * }; + * + * For plain GEM based drivers there is the DEFINE_DRM_GEM_FOPS() macro, and for + * CMA based drivers there is the DEFINE_DRM_GEM_CMA_FOPS() macro to make this + * simpler. */ static int drm_open_helper(struct file *filp, struct drm_minor *minor); @@ -111,9 +121,9 @@ static int drm_setup(struct drm_device * dev) * @inode: device inode * @filp: file pointer. * - * This function must be used by drivers as their .open() #file_operations - * method. It looks up the correct DRM device and instantiates all the per-file - * resources for it. + * This function must be used by drivers as their &file_operations.open method. + * It looks up the correct DRM device and instantiates all the per-file + * resources for it. It also calls the &drm_driver.open driver callback. * * RETURNS: * @@ -298,11 +308,6 @@ static void drm_events_release(struct drm_file *file_priv) spin_unlock_irqrestore(&dev->event_lock, flags); } -/* - * drm_legacy_dev_reinit - * - * Reinitializes a legacy/ums drm device in it's lastclose function. - */ static void drm_legacy_dev_reinit(struct drm_device *dev) { if (dev->irq_enabled) @@ -327,15 +332,6 @@ static void drm_legacy_dev_reinit(struct drm_device *dev) DRM_DEBUG("lastclose completed\n"); } -/* - * Take down the DRM device. - * - * \param dev DRM device structure. - * - * Frees every resource in \p dev. - * - * \sa drm_device - */ void drm_lastclose(struct drm_device * dev) { DRM_DEBUG("\n"); @@ -353,9 +349,11 @@ void drm_lastclose(struct drm_device * dev) * @inode: device inode * @filp: file pointer. * - * This function must be used by drivers as their .release() #file_operations - * method. It frees any resources associated with the open file, and if this is - * the last open file for the DRM device also proceeds to call drm_lastclose(). + * This function must be used by drivers as their &file_operations.release + * method. It frees any resources associated with the open file, and calls the + * &drm_driver.preclose and &drm_driver.lastclose driver callbacks. If this is + * the last open file for the DRM device also proceeds to call the + * &drm_driver.lastclose driver callback. * * RETURNS: * @@ -443,13 +441,13 @@ EXPORT_SYMBOL(drm_release); * @count: count in bytes to read * @offset: offset to read * - * This function must be used by drivers as their .read() #file_operations + * This function must be used by drivers as their &file_operations.read * method iff they use DRM events for asynchronous signalling to userspace. * Since events are used by the KMS API for vblank and page flip completion this * means all modern display drivers must use it. * - * @offset is ignore, DRM events are read like a pipe. Therefore drivers also - * must set the .llseek() #file_operation to no_llseek(). Polling support is + * @offset is ignored, DRM events are read like a pipe. Therefore drivers also + * must set the &file_operation.llseek to no_llseek(). Polling support is * provided by drm_poll(). * * This function will only ever read a full event. Therefore userspace must @@ -537,10 +535,10 @@ EXPORT_SYMBOL(drm_read); * @filp: file pointer * @wait: poll waiter table * - * This function must be used by drivers as their .read() #file_operations - * method iff they use DRM events for asynchronous signalling to userspace. - * Since events are used by the KMS API for vblank and page flip completion this - * means all modern display drivers must use it. + * This function must be used by drivers as their &file_operations.read method + * iff they use DRM events for asynchronous signalling to userspace. Since + * events are used by the KMS API for vblank and page flip completion this means + * all modern display drivers must use it. * * See also drm_read(). * @@ -650,7 +648,8 @@ EXPORT_SYMBOL(drm_event_reserve_init); * @p: tracking structure for the pending event * * This function frees the event @p initialized with drm_event_reserve_init() - * and releases any allocated space. + * and releases any allocated space. It is used to cancel an event when the + * nonblocking operation could not be submitted and needed to be aborted. */ void drm_event_cancel_free(struct drm_device *dev, struct drm_pending_event *p) diff --git a/drivers/gpu/drm/drm_gem_cma_helper.c b/drivers/gpu/drm/drm_gem_cma_helper.c index bb968e76779b..bc28e7575254 100644 --- a/drivers/gpu/drm/drm_gem_cma_helper.c +++ b/drivers/gpu/drm/drm_gem_cma_helper.c @@ -338,6 +338,9 @@ static int drm_gem_cma_mmap_obj(struct drm_gem_cma_object *cma_obj, * as their ->mmap() handler in the DRM device file's file_operations * structure. * + * Instead of directly referencing this function, drivers should use the + * DEFINE_DRM_GEM_CMA_FOPS().macro. + * * Returns: * 0 on success or a negative error code on failure. */ diff --git a/drivers/gpu/drm/drm_irq.c b/drivers/gpu/drm/drm_irq.c index 1906723af389..53a526c7b24d 100644 --- a/drivers/gpu/drm/drm_irq.c +++ b/drivers/gpu/drm/drm_irq.c @@ -978,7 +978,7 @@ static void send_vblank_event(struct drm_device *dev, e->event.tv_sec = now->tv_sec; e->event.tv_usec = now->tv_usec; - trace_drm_vblank_event_delivered(e->base.pid, e->pipe, + trace_drm_vblank_event_delivered(e->base.file_priv, e->pipe, e->event.sequence); drm_send_event_locked(dev, &e->base); @@ -1198,9 +1198,9 @@ static void drm_vblank_put(struct drm_device *dev, unsigned int pipe) if (atomic_dec_and_test(&vblank->refcount)) { if (drm_vblank_offdelay == 0) return; - else if (dev->vblank_disable_immediate || drm_vblank_offdelay < 0) + else if (drm_vblank_offdelay < 0) vblank_disable_fn((unsigned long)vblank); - else + else if (!dev->vblank_disable_immediate) mod_timer(&vblank->disable_timer, jiffies + ((drm_vblank_offdelay * HZ)/1000)); } @@ -1505,7 +1505,6 @@ static int drm_queue_vblank_event(struct drm_device *dev, unsigned int pipe, } e->pipe = pipe; - e->base.pid = current->pid; e->event.base.type = DRM_EVENT_VBLANK; e->event.base.length = sizeof(e->event); e->event.user_data = vblwait->request.signal; @@ -1534,7 +1533,7 @@ static int drm_queue_vblank_event(struct drm_device *dev, unsigned int pipe, DRM_DEBUG("event on vblank count %u, current %u, crtc %u\n", vblwait->request.sequence, seq, pipe); - trace_drm_vblank_event_queued(current->pid, pipe, + trace_drm_vblank_event_queued(file_priv, pipe, vblwait->request.sequence); e->event.sequence = vblwait->request.sequence; @@ -1611,7 +1610,7 @@ int drm_wait_vblank(struct drm_device *dev, void *data, ret = drm_vblank_get(dev, pipe); if (ret) { - DRM_DEBUG("failed to acquire vblank counter, %d\n", ret); + DRM_DEBUG("crtc %d failed to acquire vblank counter, %d\n", pipe, ret); return ret; } seq = drm_vblank_count(dev, pipe); @@ -1639,13 +1638,15 @@ int drm_wait_vblank(struct drm_device *dev, void *data, return drm_queue_vblank_event(dev, pipe, vblwait, file_priv); } - DRM_DEBUG("waiting on vblank count %u, crtc %u\n", - vblwait->request.sequence, pipe); - DRM_WAIT_ON(ret, vblank->queue, 3 * HZ, - (((drm_vblank_count(dev, pipe) - - vblwait->request.sequence) <= (1 << 23)) || - !vblank->enabled || - !dev->irq_enabled)); + if (vblwait->request.sequence != seq) { + DRM_DEBUG("waiting on vblank count %u, crtc %u\n", + vblwait->request.sequence, pipe); + DRM_WAIT_ON(ret, vblank->queue, 3 * HZ, + (((drm_vblank_count(dev, pipe) - + vblwait->request.sequence) <= (1 << 23)) || + !vblank->enabled || + !dev->irq_enabled)); + } if (ret != -EINTR) { struct timeval now; @@ -1654,10 +1655,10 @@ int drm_wait_vblank(struct drm_device *dev, void *data, vblwait->reply.tval_sec = now.tv_sec; vblwait->reply.tval_usec = now.tv_usec; - DRM_DEBUG("returning %u to client\n", - vblwait->reply.sequence); + DRM_DEBUG("crtc %d returning %u to client\n", + pipe, vblwait->reply.sequence); } else { - DRM_DEBUG("vblank wait interrupted by signal\n"); + DRM_DEBUG("crtc %d vblank wait interrupted by signal\n", pipe); } done: @@ -1735,6 +1736,16 @@ bool drm_handle_vblank(struct drm_device *dev, unsigned int pipe) wake_up(&vblank->queue); drm_handle_vblank_events(dev, pipe); + /* With instant-off, we defer disabling the interrupt until after + * we finish processing the following vblank. The disable has to + * be last (after drm_handle_vblank_events) so that the timestamp + * is always accurate. + */ + if (dev->vblank_disable_immediate && + drm_vblank_offdelay > 0 && + !atomic_read(&vblank->refcount)) + vblank_disable_fn((unsigned long)vblank); + spin_unlock_irqrestore(&dev->event_lock, irqflags); return true; diff --git a/drivers/gpu/drm/drm_scdc_helper.c b/drivers/gpu/drm/drm_scdc_helper.c new file mode 100644 index 000000000000..3cd96a95736d --- /dev/null +++ b/drivers/gpu/drm/drm_scdc_helper.c @@ -0,0 +1,244 @@ +/* + * Copyright (c) 2015 NVIDIA Corporation. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sub license, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include <linux/slab.h> +#include <linux/delay.h> + +#include <drm/drm_scdc_helper.h> +#include <drm/drmP.h> + +/** + * DOC: scdc helpers + * + * Status and Control Data Channel (SCDC) is a mechanism introduced by the + * HDMI 2.0 specification. It is a point-to-point protocol that allows the + * HDMI source and HDMI sink to exchange data. The same I2C interface that + * is used to access EDID serves as the transport mechanism for SCDC. + */ + +#define SCDC_I2C_SLAVE_ADDRESS 0x54 + +/** + * drm_scdc_read - read a block of data from SCDC + * @adapter: I2C controller + * @offset: start offset of block to read + * @buffer: return location for the block to read + * @size: size of the block to read + * + * Reads a block of data from SCDC, starting at a given offset. + * + * Returns: + * 0 on success, negative error code on failure. + */ +ssize_t drm_scdc_read(struct i2c_adapter *adapter, u8 offset, void *buffer, + size_t size) +{ + int ret; + struct i2c_msg msgs[2] = { + { + .addr = SCDC_I2C_SLAVE_ADDRESS, + .flags = 0, + .len = 1, + .buf = &offset, + }, { + .addr = SCDC_I2C_SLAVE_ADDRESS, + .flags = I2C_M_RD, + .len = size, + .buf = buffer, + } + }; + + ret = i2c_transfer(adapter, msgs, ARRAY_SIZE(msgs)); + if (ret < 0) + return ret; + if (ret != ARRAY_SIZE(msgs)) + return -EPROTO; + + return 0; +} +EXPORT_SYMBOL(drm_scdc_read); + +/** + * drm_scdc_write - write a block of data to SCDC + * @adapter: I2C controller + * @offset: start offset of block to write + * @buffer: block of data to write + * @size: size of the block to write + * + * Writes a block of data to SCDC, starting at a given offset. + * + * Returns: + * 0 on success, negative error code on failure. + */ +ssize_t drm_scdc_write(struct i2c_adapter *adapter, u8 offset, + const void *buffer, size_t size) +{ + struct i2c_msg msg = { + .addr = SCDC_I2C_SLAVE_ADDRESS, + .flags = 0, + .len = 1 + size, + .buf = NULL, + }; + void *data; + int err; + + data = kmalloc(1 + size, GFP_TEMPORARY); + if (!data) + return -ENOMEM; + + msg.buf = data; + + memcpy(data, &offset, sizeof(offset)); + memcpy(data + 1, buffer, size); + + err = i2c_transfer(adapter, &msg, 1); + + kfree(data); + + if (err < 0) + return err; + if (err != 1) + return -EPROTO; + + return 0; +} +EXPORT_SYMBOL(drm_scdc_write); + +/** + * drm_scdc_check_scrambling_status - what is status of scrambling? + * @adapter: I2C adapter for DDC channel + * + * Reads the scrambler status over SCDC, and checks the + * scrambling status. + * + * Returns: + * True if the scrambling is enabled, false otherwise. + */ + +bool drm_scdc_get_scrambling_status(struct i2c_adapter *adapter) +{ + u8 status; + int ret; + + ret = drm_scdc_readb(adapter, SCDC_SCRAMBLER_STATUS, &status); + if (ret < 0) { + DRM_ERROR("Failed to read scrambling status, error %d\n", ret); + return false; + } + + return status & SCDC_SCRAMBLING_STATUS; +} +EXPORT_SYMBOL(drm_scdc_get_scrambling_status); + +/** + * drm_scdc_set_scrambling - enable scrambling + * @adapter: I2C adapter for DDC channel + * @enable: bool to indicate if scrambling is to be enabled/disabled + * + * Writes the TMDS config register over SCDC channel, and: + * enables scrambling when enable = 1 + * disables scrambling when enable = 0 + * + * Returns: + * True if scrambling is set/reset successfully, false otherwise. + */ + +bool drm_scdc_set_scrambling(struct i2c_adapter *adapter, bool enable) +{ + u8 config; + int ret; + + ret = drm_scdc_readb(adapter, SCDC_TMDS_CONFIG, &config); + if (ret < 0) { + DRM_ERROR("Failed to read tmds config, err=%d\n", ret); + return false; + } + + if (enable) + config |= SCDC_SCRAMBLING_ENABLE; + else + config &= ~SCDC_SCRAMBLING_ENABLE; + + ret = drm_scdc_writeb(adapter, SCDC_TMDS_CONFIG, config); + if (ret < 0) { + DRM_ERROR("Failed to enable scrambling, error %d\n", ret); + return false; + } + + return true; +} +EXPORT_SYMBOL(drm_scdc_set_scrambling); + +/** + * drm_scdc_set_high_tmds_clock_ratio - set TMDS clock ratio + * @adapter: I2C adapter for DDC channel + * @set: ret or reset the high clock ratio + * + * TMDS clock ratio calculations go like this: + * TMDS character = 10 bit TMDS encoded value + * TMDS character rate = The rate at which TMDS characters are transmitted(Mcsc) + * TMDS bit rate = 10x TMDS character rate + * As per the spec: + * TMDS clock rate for pixel clock < 340 MHz = 1x the character rate + * = 1/10 pixel clock rate + * TMDS clock rate for pixel clock > 340 MHz = 0.25x the character rate + * = 1/40 pixel clock rate + * + * Writes to the TMDS config register over SCDC channel, and: + * sets TMDS clock ratio to 1/40 when set = 1 + * sets TMDS clock ratio to 1/10 when set = 0 + * + * Returns: + * True if write is successful, false otherwise. + */ +bool drm_scdc_set_high_tmds_clock_ratio(struct i2c_adapter *adapter, bool set) +{ + u8 config; + int ret; + + ret = drm_scdc_readb(adapter, SCDC_TMDS_CONFIG, &config); + if (ret < 0) { + DRM_ERROR("Failed to read tmds config, err=%d\n", ret); + return false; + } + + if (set) + config |= SCDC_TMDS_BIT_CLOCK_RATIO_BY_40; + else + config &= ~SCDC_TMDS_BIT_CLOCK_RATIO_BY_40; + + ret = drm_scdc_writeb(adapter, SCDC_TMDS_CONFIG, config); + if (ret < 0) { + DRM_ERROR("Failed to set TMDS clock ratio, error %d\n", ret); + return false; + } + + /* + * The spec says that a source should wait minimum 1ms and maximum + * 100ms after writing the TMDS config for clock ratio. Lets allow a + * wait of upto 2ms here. + */ + usleep_range(1000, 2000); + return true; +} +EXPORT_SYMBOL(drm_scdc_set_high_tmds_clock_ratio); diff --git a/drivers/gpu/drm/drm_trace.h b/drivers/gpu/drm/drm_trace.h index ce3c42813fbb..14c5a777682e 100644 --- a/drivers/gpu/drm/drm_trace.h +++ b/drivers/gpu/drm/drm_trace.h @@ -24,36 +24,36 @@ TRACE_EVENT(drm_vblank_event, ); TRACE_EVENT(drm_vblank_event_queued, - TP_PROTO(pid_t pid, int crtc, unsigned int seq), - TP_ARGS(pid, crtc, seq), + TP_PROTO(struct drm_file *file, int crtc, unsigned int seq), + TP_ARGS(file, crtc, seq), TP_STRUCT__entry( - __field(pid_t, pid) + __field(struct drm_file *, file) __field(int, crtc) __field(unsigned int, seq) ), TP_fast_assign( - __entry->pid = pid; + __entry->file = file; __entry->crtc = crtc; __entry->seq = seq; ), - TP_printk("pid=%d, crtc=%d, seq=%u", __entry->pid, __entry->crtc, \ + TP_printk("file=%p, crtc=%d, seq=%u", __entry->file, __entry->crtc, \ __entry->seq) ); TRACE_EVENT(drm_vblank_event_delivered, - TP_PROTO(pid_t pid, int crtc, unsigned int seq), - TP_ARGS(pid, crtc, seq), + TP_PROTO(struct drm_file *file, int crtc, unsigned int seq), + TP_ARGS(file, crtc, seq), TP_STRUCT__entry( - __field(pid_t, pid) + __field(struct drm_file *, file) __field(int, crtc) __field(unsigned int, seq) ), TP_fast_assign( - __entry->pid = pid; + __entry->file = file; __entry->crtc = crtc; __entry->seq = seq; ), - TP_printk("pid=%d, crtc=%d, seq=%u", __entry->pid, __entry->crtc, \ + TP_printk("file=%p, crtc=%d, seq=%u", __entry->file, __entry->crtc, \ __entry->seq) ); diff --git a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.c b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.c index b5391c124c64..6e00f4b267f1 100644 --- a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.c +++ b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.c @@ -161,17 +161,7 @@ static void fsl_dcu_drm_lastclose(struct drm_device *dev) drm_fbdev_cma_restore_mode(fsl_dev->fbdev); } -static const struct file_operations fsl_dcu_drm_fops = { - .owner = THIS_MODULE, - .open = drm_open, - .release = drm_release, - .unlocked_ioctl = drm_ioctl, - .compat_ioctl = drm_compat_ioctl, - .poll = drm_poll, - .read = drm_read, - .llseek = no_llseek, - .mmap = drm_gem_cma_mmap, -}; +DEFINE_DRM_GEM_CMA_FOPS(fsl_dcu_drm_fops); static struct drm_driver fsl_dcu_drm_driver = { .driver_features = DRIVER_HAVE_IRQ | DRIVER_GEM | DRIVER_MODESET diff --git a/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c b/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c index 7ec93aec7e88..df4f50713e54 100644 --- a/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c +++ b/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c @@ -146,17 +146,7 @@ err_mode_config_cleanup: return ret; } -static const struct file_operations kirin_drm_fops = { - .owner = THIS_MODULE, - .open = drm_open, - .release = drm_release, - .unlocked_ioctl = drm_ioctl, - .compat_ioctl = drm_compat_ioctl, - .poll = drm_poll, - .read = drm_read, - .llseek = no_llseek, - .mmap = drm_gem_cma_mmap, -}; +DEFINE_DRM_GEM_CMA_FOPS(kirin_drm_fops); static int kirin_gem_cma_dumb_create(struct drm_file *file, struct drm_device *dev, diff --git a/drivers/gpu/drm/imx/imx-drm-core.c b/drivers/gpu/drm/imx/imx-drm-core.c index b6dbcd17f1e6..1888bf3920fc 100644 --- a/drivers/gpu/drm/imx/imx-drm-core.c +++ b/drivers/gpu/drm/imx/imx-drm-core.c @@ -53,16 +53,7 @@ static void imx_drm_driver_lastclose(struct drm_device *drm) drm_fbdev_cma_restore_mode(imxdrm->fbhelper); } -static const struct file_operations imx_drm_driver_fops = { - .owner = THIS_MODULE, - .open = drm_open, - .release = drm_release, - .unlocked_ioctl = drm_ioctl, - .mmap = drm_gem_cma_mmap, - .poll = drm_poll, - .read = drm_read, - .llseek = noop_llseek, -}; +DEFINE_DRM_GEM_CMA_FOPS(imx_drm_driver_fops); void imx_drm_connector_destroy(struct drm_connector *connector) { diff --git a/drivers/gpu/drm/meson/meson_drv.c b/drivers/gpu/drm/meson/meson_drv.c index 8d17d0e59cbe..bc562a07847b 100644 --- a/drivers/gpu/drm/meson/meson_drv.c +++ b/drivers/gpu/drm/meson/meson_drv.c @@ -91,19 +91,7 @@ static irqreturn_t meson_irq(int irq, void *arg) return IRQ_HANDLED; } -static const struct file_operations fops = { - .owner = THIS_MODULE, - .open = drm_open, - .release = drm_release, - .unlocked_ioctl = drm_ioctl, -#ifdef CONFIG_COMPAT - .compat_ioctl = drm_compat_ioctl, -#endif - .poll = drm_poll, - .read = drm_read, - .llseek = no_llseek, - .mmap = drm_gem_cma_mmap, -}; +DEFINE_DRM_GEM_CMA_FOPS(fops); static struct drm_driver meson_driver = { .driver_features = DRIVER_HAVE_IRQ | DRIVER_GEM | diff --git a/drivers/gpu/drm/msm/msm_drv.h b/drivers/gpu/drm/msm/msm_drv.h index b51fb0d70f43..b885c3d5ae4d 100644 --- a/drivers/gpu/drm/msm/msm_drv.h +++ b/drivers/gpu/drm/msm/msm_drv.h @@ -311,6 +311,8 @@ void msm_perf_debugfs_cleanup(struct msm_drm_private *priv); #else static inline int msm_debugfs_late_init(struct drm_device *dev) { return 0; } static inline void msm_rd_dump_submit(struct msm_gem_submit *submit) {} +static inline void msm_rd_debugfs_cleanup(struct msm_drm_private *priv) {} +static inline void msm_perf_debugfs_cleanup(struct msm_drm_private *priv) {} #endif struct clk *msm_clk_get(struct platform_device *pdev, const char *name); diff --git a/drivers/gpu/drm/mxsfb/mxsfb_drv.c b/drivers/gpu/drm/mxsfb/mxsfb_drv.c index a4633ada8429..5ac712325c75 100644 --- a/drivers/gpu/drm/mxsfb/mxsfb_drv.c +++ b/drivers/gpu/drm/mxsfb/mxsfb_drv.c @@ -319,19 +319,7 @@ static irqreturn_t mxsfb_irq_handler(int irq, void *data) return IRQ_HANDLED; } -static const struct file_operations fops = { - .owner = THIS_MODULE, - .open = drm_open, - .release = drm_release, - .unlocked_ioctl = drm_ioctl, -#ifdef CONFIG_COMPAT - .compat_ioctl = drm_compat_ioctl, -#endif - .poll = drm_poll, - .read = drm_read, - .llseek = noop_llseek, - .mmap = drm_gem_cma_mmap, -}; +DEFINE_DRM_GEM_CMA_FOPS(fops); static struct drm_driver mxsfb_driver = { .driver_features = DRIVER_GEM | DRIVER_MODESET | diff --git a/drivers/gpu/drm/nouveau/nouveau_usif.c b/drivers/gpu/drm/nouveau/nouveau_usif.c index afbdbed1a690..9dc10b17ad34 100644 --- a/drivers/gpu/drm/nouveau/nouveau_usif.c +++ b/drivers/gpu/drm/nouveau/nouveau_usif.c @@ -211,7 +211,6 @@ usif_notify_get(struct drm_file *f, void *data, u32 size, void *argv, u32 argc) goto done; ntfy->p->base.event = &ntfy->p->e.base; ntfy->p->base.file_priv = f; - ntfy->p->base.pid = current->pid; ntfy->p->e.base.type = DRM_NOUVEAU_EVENT_NVIF; ntfy->p->e.base.length = sizeof(ntfy->p->e.base) + ntfy->reply; diff --git a/drivers/gpu/drm/qxl/qxl_display.c b/drivers/gpu/drm/qxl/qxl_display.c index 9548bb58d3bc..058340a002c2 100644 --- a/drivers/gpu/drm/qxl/qxl_display.c +++ b/drivers/gpu/drm/qxl/qxl_display.c @@ -572,6 +572,8 @@ static void qxl_cursor_atomic_update(struct drm_plane *plane, ret = qxl_alloc_release_reserved(qdev, sizeof(*cmd), QXL_RELEASE_CURSOR_CMD, &release, NULL); + if (ret) + return; cmd = (struct qxl_cursor_cmd *) qxl_release_map(qdev, release); diff --git a/drivers/gpu/drm/rcar-du/rcar_du_drv.c b/drivers/gpu/drm/rcar-du/rcar_du_drv.c index 192346d4fb34..62a3b3e32153 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_drv.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_drv.c @@ -226,17 +226,7 @@ static void rcar_du_lastclose(struct drm_device *dev) drm_fbdev_cma_restore_mode(rcdu->fbdev); } -static const struct file_operations rcar_du_fops = { - .owner = THIS_MODULE, - .open = drm_open, - .release = drm_release, - .unlocked_ioctl = drm_ioctl, - .compat_ioctl = drm_compat_ioctl, - .poll = drm_poll, - .read = drm_read, - .llseek = no_llseek, - .mmap = drm_gem_cma_mmap, -}; +DEFINE_DRM_GEM_CMA_FOPS(rcar_du_fops); static struct drm_driver rcar_du_driver = { .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME diff --git a/drivers/gpu/drm/rockchip/cdn-dp-core.c b/drivers/gpu/drm/rockchip/cdn-dp-core.c index fd79a70b8552..9edb8dc1ea14 100644 --- a/drivers/gpu/drm/rockchip/cdn-dp-core.c +++ b/drivers/gpu/drm/rockchip/cdn-dp-core.c @@ -94,7 +94,7 @@ static int cdn_dp_grf_write(struct cdn_dp_device *dp, static int cdn_dp_clk_enable(struct cdn_dp_device *dp) { int ret; - u32 rate; + unsigned long rate; ret = clk_prepare_enable(dp->pclk); if (ret < 0) { @@ -123,7 +123,8 @@ static int cdn_dp_clk_enable(struct cdn_dp_device *dp) rate = clk_get_rate(dp->core_clk); if (!rate) { - DRM_DEV_ERROR(dp->dev, "get clk rate failed: %d\n", rate); + DRM_DEV_ERROR(dp->dev, "get clk rate failed\n"); + ret = -EINVAL; goto err_set_rate; } diff --git a/drivers/gpu/drm/rockchip/cdn-dp-reg.c b/drivers/gpu/drm/rockchip/cdn-dp-reg.c index 319dbbaa3609..b14d211f6c21 100644 --- a/drivers/gpu/drm/rockchip/cdn-dp-reg.c +++ b/drivers/gpu/drm/rockchip/cdn-dp-reg.c @@ -29,7 +29,7 @@ #define LINK_TRAINING_RETRY_MS 20 #define LINK_TRAINING_TIMEOUT_MS 500 -void cdn_dp_set_fw_clk(struct cdn_dp_device *dp, u32 clk) +void cdn_dp_set_fw_clk(struct cdn_dp_device *dp, unsigned long clk) { writel(clk / 1000000, dp->regs + SW_CLK_H); } @@ -671,6 +671,10 @@ int cdn_dp_config_video(struct cdn_dp_device *dp) rem = do_div(symbol, 1000); if (tu_size_reg > 64) { ret = -EINVAL; + DRM_DEV_ERROR(dp->dev, + "tu error, clk:%d, lanes:%d, rate:%d\n", + mode->clock, dp->link.num_lanes, + link_rate); goto err_config_video; } } while ((symbol <= 1) || (tu_size_reg - symbol < 4) || diff --git a/drivers/gpu/drm/rockchip/cdn-dp-reg.h b/drivers/gpu/drm/rockchip/cdn-dp-reg.h index b5f215324694..c4bbb4a83319 100644 --- a/drivers/gpu/drm/rockchip/cdn-dp-reg.h +++ b/drivers/gpu/drm/rockchip/cdn-dp-reg.h @@ -121,12 +121,11 @@ /* dptx phy addr */ #define DP_TX_PHY_CONFIG_REG 0x2000 -#define DP_TX_PHY_STATUS_REG 0x2004 -#define DP_TX_PHY_SW_RESET 0x2008 -#define DP_TX_PHY_SCRAMBLER_SEED 0x200c -#define DP_TX_PHY_TRAINING_01_04 0x2010 -#define DP_TX_PHY_TRAINING_05_08 0x2014 -#define DP_TX_PHY_TRAINING_09_10 0x2018 +#define DP_TX_PHY_SW_RESET 0x2004 +#define DP_TX_PHY_SCRAMBLER_SEED 0x2008 +#define DP_TX_PHY_TRAINING_01_04 0x200c +#define DP_TX_PHY_TRAINING_05_08 0x2010 +#define DP_TX_PHY_TRAINING_09_10 0x2014 #define TEST_COR 0x23fc /* dptx hpd addr */ @@ -462,7 +461,7 @@ enum vic_bt_type { void cdn_dp_clock_reset(struct cdn_dp_device *dp); -void cdn_dp_set_fw_clk(struct cdn_dp_device *dp, u32 clk); +void cdn_dp_set_fw_clk(struct cdn_dp_device *dp, unsigned long clk); int cdn_dp_load_firmware(struct cdn_dp_device *dp, const u32 *i_mem, u32 i_size, const u32 *d_mem, u32 d_size); int cdn_dp_set_firmware_active(struct cdn_dp_device *dp, bool enable); diff --git a/drivers/gpu/drm/shmobile/shmob_drm_drv.c b/drivers/gpu/drm/shmobile/shmob_drm_drv.c index 1c7b318b8998..800d1d2c435d 100644 --- a/drivers/gpu/drm/shmobile/shmob_drm_drv.c +++ b/drivers/gpu/drm/shmobile/shmob_drm_drv.c @@ -127,17 +127,7 @@ static irqreturn_t shmob_drm_irq(int irq, void *arg) return IRQ_HANDLED; } -static const struct file_operations shmob_drm_fops = { - .owner = THIS_MODULE, - .open = drm_open, - .release = drm_release, - .unlocked_ioctl = drm_ioctl, - .compat_ioctl = drm_compat_ioctl, - .poll = drm_poll, - .read = drm_read, - .llseek = no_llseek, - .mmap = drm_gem_cma_mmap, -}; +DEFINE_DRM_GEM_CMA_FOPS(shmob_drm_fops); static struct drm_driver shmob_drm_driver = { .driver_features = DRIVER_HAVE_IRQ | DRIVER_GEM | DRIVER_MODESET diff --git a/drivers/gpu/drm/sti/sti_drv.c b/drivers/gpu/drm/sti/sti_drv.c index 6003c664ba0b..a4b574283269 100644 --- a/drivers/gpu/drm/sti/sti_drv.c +++ b/drivers/gpu/drm/sti/sti_drv.c @@ -167,16 +167,7 @@ static void sti_mode_config_init(struct drm_device *dev) dev->mode_config.funcs = &sti_mode_config_funcs; } -static const struct file_operations sti_driver_fops = { - .owner = THIS_MODULE, - .open = drm_open, - .mmap = drm_gem_cma_mmap, - .poll = drm_poll, - .read = drm_read, - .unlocked_ioctl = drm_ioctl, - .compat_ioctl = drm_compat_ioctl, - .release = drm_release, -}; +DEFINE_DRM_GEM_CMA_FOPS(sti_driver_fops); static struct drm_driver sti_driver = { .driver_features = DRIVER_MODESET | diff --git a/drivers/gpu/drm/sun4i/sun4i_drv.c b/drivers/gpu/drm/sun4i/sun4i_drv.c index 9ccf7c4deb6d..329ea56106a5 100644 --- a/drivers/gpu/drm/sun4i/sun4i_drv.c +++ b/drivers/gpu/drm/sun4i/sun4i_drv.c @@ -25,17 +25,7 @@ #include "sun4i_framebuffer.h" #include "sun4i_layer.h" -static const struct file_operations sun4i_drv_fops = { - .owner = THIS_MODULE, - .open = drm_open, - .release = drm_release, - .unlocked_ioctl = drm_ioctl, - .compat_ioctl = drm_compat_ioctl, - .poll = drm_poll, - .read = drm_read, - .llseek = no_llseek, - .mmap = drm_gem_cma_mmap, -}; +DEFINE_DRM_GEM_CMA_FOPS(sun4i_drv_fops); static struct drm_driver sun4i_drv_driver = { .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME | DRIVER_ATOMIC, diff --git a/drivers/gpu/drm/tilcdc/tilcdc_drv.c b/drivers/gpu/drm/tilcdc/tilcdc_drv.c index 5b4ed0ea7768..d7ae5be56d12 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_drv.c +++ b/drivers/gpu/drm/tilcdc/tilcdc_drv.c @@ -529,17 +529,7 @@ static int tilcdc_debugfs_init(struct drm_minor *minor) } #endif -static const struct file_operations fops = { - .owner = THIS_MODULE, - .open = drm_open, - .release = drm_release, - .unlocked_ioctl = drm_ioctl, - .compat_ioctl = drm_compat_ioctl, - .poll = drm_poll, - .read = drm_read, - .llseek = no_llseek, - .mmap = drm_gem_cma_mmap, -}; +DEFINE_DRM_GEM_CMA_FOPS(fops); static struct drm_driver tilcdc_driver = { .driver_features = (DRIVER_HAVE_IRQ | DRIVER_GEM | DRIVER_MODESET | diff --git a/drivers/gpu/drm/tinydrm/mipi-dbi.c b/drivers/gpu/drm/tinydrm/mipi-dbi.c index 29c0939f5247..f4eb412f3604 100644 --- a/drivers/gpu/drm/tinydrm/mipi-dbi.c +++ b/drivers/gpu/drm/tinydrm/mipi-dbi.c @@ -590,7 +590,7 @@ static int mipi_dbi_spi1e_transfer(struct mipi_dbi *mipi, int dc, ret = spi_sync(spi, &m); if (ret) return ret; - }; + } return 0; } @@ -654,7 +654,7 @@ static int mipi_dbi_spi1_transfer(struct mipi_dbi *mipi, int dc, ret = spi_sync(spi, &m); if (ret) return ret; - }; + } return 0; } diff --git a/drivers/gpu/drm/vc4/Kconfig b/drivers/gpu/drm/vc4/Kconfig index e1517d07cb7d..973b4203c0b2 100644 --- a/drivers/gpu/drm/vc4/Kconfig +++ b/drivers/gpu/drm/vc4/Kconfig @@ -2,11 +2,15 @@ config DRM_VC4 tristate "Broadcom VC4 Graphics" depends on ARCH_BCM2835 || COMPILE_TEST depends on DRM + depends on SND && SND_SOC depends on COMMON_CLK select DRM_KMS_HELPER select DRM_KMS_CMA_HELPER select DRM_GEM_CMA_HELPER select DRM_PANEL + select SND_PCM + select SND_PCM_ELD + select SND_SOC_GENERIC_DMAENGINE_PCM select DRM_MIPI_DSI help Choose this option if you have a system that has a Broadcom diff --git a/drivers/gpu/drm/vc4/vc4_drv.c b/drivers/gpu/drm/vc4/vc4_drv.c index 205c1961ffb4..61e674baf3a6 100644 --- a/drivers/gpu/drm/vc4/vc4_drv.c +++ b/drivers/gpu/drm/vc4/vc4_drv.c @@ -349,26 +349,20 @@ static struct platform_driver vc4_platform_driver = { static int __init vc4_drm_register(void) { - int i, ret; + int ret; + + ret = platform_register_drivers(component_drivers, + ARRAY_SIZE(component_drivers)); + if (ret) + return ret; - for (i = 0; i < ARRAY_SIZE(component_drivers); i++) { - ret = platform_driver_register(component_drivers[i]); - if (ret) { - while (--i >= 0) - platform_driver_unregister(component_drivers[i]); - return ret; - } - } return platform_driver_register(&vc4_platform_driver); } static void __exit vc4_drm_unregister(void) { - int i; - - for (i = ARRAY_SIZE(component_drivers) - 1; i >= 0; i--) - platform_driver_unregister(component_drivers[i]); - + platform_unregister_drivers(component_drivers, + ARRAY_SIZE(component_drivers)); platform_driver_unregister(&vc4_platform_driver); } diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c index 1be1e8304720..e9cbe269710b 100644 --- a/drivers/gpu/drm/vc4/vc4_hdmi.c +++ b/drivers/gpu/drm/vc4/vc4_hdmi.c @@ -48,11 +48,27 @@ #include "linux/clk.h" #include "linux/component.h" #include "linux/i2c.h" +#include "linux/of_address.h" #include "linux/of_gpio.h" #include "linux/of_platform.h" +#include "linux/rational.h" +#include "sound/dmaengine_pcm.h" +#include "sound/pcm_drm_eld.h" +#include "sound/pcm_params.h" +#include "sound/soc.h" #include "vc4_drv.h" #include "vc4_regs.h" +/* HDMI audio information */ +struct vc4_hdmi_audio { + struct snd_soc_card card; + struct snd_soc_dai_link link; + int samplerate; + int channels; + struct snd_dmaengine_dai_dma_data dma_data; + struct snd_pcm_substream *substream; +}; + /* General HDMI hardware state. */ struct vc4_hdmi { struct platform_device *pdev; @@ -60,6 +76,8 @@ struct vc4_hdmi { struct drm_encoder *encoder; struct drm_connector *connector; + struct vc4_hdmi_audio audio; + struct i2c_adapter *ddc; void __iomem *hdmicore_regs; void __iomem *hd_regs; @@ -115,6 +133,10 @@ static const struct { HDMI_REG(VC4_HDMI_SW_RESET_CONTROL), HDMI_REG(VC4_HDMI_HOTPLUG_INT), HDMI_REG(VC4_HDMI_HOTPLUG), + HDMI_REG(VC4_HDMI_MAI_CHANNEL_MAP), + HDMI_REG(VC4_HDMI_MAI_CONFIG), + HDMI_REG(VC4_HDMI_MAI_FORMAT), + HDMI_REG(VC4_HDMI_AUDIO_PACKET_CONFIG), HDMI_REG(VC4_HDMI_RAM_PACKET_CONFIG), HDMI_REG(VC4_HDMI_HORZA), HDMI_REG(VC4_HDMI_HORZB), @@ -125,6 +147,7 @@ static const struct { HDMI_REG(VC4_HDMI_VERTB0), HDMI_REG(VC4_HDMI_VERTB1), HDMI_REG(VC4_HDMI_TX_PHY_RESET_CTL), + HDMI_REG(VC4_HDMI_TX_PHY_CTL0), }; static const struct { @@ -133,6 +156,9 @@ static const struct { } hd_regs[] = { HDMI_REG(VC4_HD_M_CTL), HDMI_REG(VC4_HD_MAI_CTL), + HDMI_REG(VC4_HD_MAI_THR), + HDMI_REG(VC4_HD_MAI_FMT), + HDMI_REG(VC4_HD_MAI_SMP), HDMI_REG(VC4_HD_VID_CTL), HDMI_REG(VC4_HD_CSC_CTL), HDMI_REG(VC4_HD_FRAME_COUNT), @@ -232,6 +258,7 @@ static int vc4_hdmi_connector_get_modes(struct drm_connector *connector) drm_mode_connector_update_edid_property(connector, edid); ret = drm_add_edid_modes(connector, edid); + drm_edid_to_eld(connector, edid); return ret; } @@ -317,7 +344,7 @@ static void vc4_hdmi_write_infoframe(struct drm_encoder *encoder, struct drm_device *dev = encoder->dev; struct vc4_dev *vc4 = to_vc4_dev(dev); u32 packet_id = frame->any.type - 0x80; - u32 packet_reg = VC4_HDMI_GCP_0 + VC4_HDMI_PACKET_STRIDE * packet_id; + u32 packet_reg = VC4_HDMI_RAM_PACKET(packet_id); uint8_t buffer[VC4_HDMI_PACKET_STRIDE]; ssize_t len, i; int ret; @@ -398,6 +425,24 @@ static void vc4_hdmi_set_spd_infoframe(struct drm_encoder *encoder) vc4_hdmi_write_infoframe(encoder, &frame); } +static void vc4_hdmi_set_audio_infoframe(struct drm_encoder *encoder) +{ + struct drm_device *drm = encoder->dev; + struct vc4_dev *vc4 = drm->dev_private; + struct vc4_hdmi *hdmi = vc4->hdmi; + union hdmi_infoframe frame; + int ret; + + ret = hdmi_audio_infoframe_init(&frame.audio); + + frame.audio.coding_type = HDMI_AUDIO_CODING_TYPE_STREAM; + frame.audio.sample_frequency = HDMI_AUDIO_SAMPLE_FREQUENCY_STREAM; + frame.audio.sample_size = HDMI_AUDIO_SAMPLE_SIZE_STREAM; + frame.audio.channels = hdmi->audio.channels; + + vc4_hdmi_write_infoframe(encoder, &frame); +} + static void vc4_hdmi_set_infoframes(struct drm_encoder *encoder) { vc4_hdmi_set_avi_infoframe(encoder); @@ -606,6 +651,447 @@ static const struct drm_encoder_helper_funcs vc4_hdmi_encoder_helper_funcs = { .enable = vc4_hdmi_encoder_enable, }; +/* HDMI audio codec callbacks */ +static void vc4_hdmi_audio_set_mai_clock(struct vc4_hdmi *hdmi) +{ + struct drm_device *drm = hdmi->encoder->dev; + struct vc4_dev *vc4 = to_vc4_dev(drm); + u32 hsm_clock = clk_get_rate(hdmi->hsm_clock); + unsigned long n, m; + + rational_best_approximation(hsm_clock, hdmi->audio.samplerate, + VC4_HD_MAI_SMP_N_MASK >> + VC4_HD_MAI_SMP_N_SHIFT, + (VC4_HD_MAI_SMP_M_MASK >> + VC4_HD_MAI_SMP_M_SHIFT) + 1, + &n, &m); + + HD_WRITE(VC4_HD_MAI_SMP, + VC4_SET_FIELD(n, VC4_HD_MAI_SMP_N) | + VC4_SET_FIELD(m - 1, VC4_HD_MAI_SMP_M)); +} + +static void vc4_hdmi_set_n_cts(struct vc4_hdmi *hdmi) +{ + struct drm_encoder *encoder = hdmi->encoder; + struct drm_crtc *crtc = encoder->crtc; + struct drm_device *drm = encoder->dev; + struct vc4_dev *vc4 = to_vc4_dev(drm); + const struct drm_display_mode *mode = &crtc->state->adjusted_mode; + u32 samplerate = hdmi->audio.samplerate; + u32 n, cts; + u64 tmp; + + n = 128 * samplerate / 1000; + tmp = (u64)(mode->clock * 1000) * n; + do_div(tmp, 128 * samplerate); + cts = tmp; + + HDMI_WRITE(VC4_HDMI_CRP_CFG, + VC4_HDMI_CRP_CFG_EXTERNAL_CTS_EN | + VC4_SET_FIELD(n, VC4_HDMI_CRP_CFG_N)); + + /* + * We could get slightly more accurate clocks in some cases by + * providing a CTS_1 value. The two CTS values are alternated + * between based on the period fields + */ + HDMI_WRITE(VC4_HDMI_CTS_0, cts); + HDMI_WRITE(VC4_HDMI_CTS_1, cts); +} + +static inline struct vc4_hdmi *dai_to_hdmi(struct snd_soc_dai *dai) +{ + struct snd_soc_card *card = snd_soc_dai_get_drvdata(dai); + + return snd_soc_card_get_drvdata(card); +} + +static int vc4_hdmi_audio_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct vc4_hdmi *hdmi = dai_to_hdmi(dai); + struct drm_encoder *encoder = hdmi->encoder; + struct vc4_dev *vc4 = to_vc4_dev(encoder->dev); + int ret; + + if (hdmi->audio.substream && hdmi->audio.substream != substream) + return -EINVAL; + + hdmi->audio.substream = substream; + + /* + * If the HDMI encoder hasn't probed, or the encoder is + * currently in DVI mode, treat the codec dai as missing. + */ + if (!encoder->crtc || !(HDMI_READ(VC4_HDMI_RAM_PACKET_CONFIG) & + VC4_HDMI_RAM_PACKET_ENABLE)) + return -ENODEV; + + ret = snd_pcm_hw_constraint_eld(substream->runtime, + hdmi->connector->eld); + if (ret) + return ret; + + return 0; +} + +static int vc4_hdmi_audio_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + return 0; +} + +static void vc4_hdmi_audio_reset(struct vc4_hdmi *hdmi) +{ + struct drm_encoder *encoder = hdmi->encoder; + struct drm_device *drm = encoder->dev; + struct device *dev = &hdmi->pdev->dev; + struct vc4_dev *vc4 = to_vc4_dev(drm); + int ret; + + ret = vc4_hdmi_stop_packet(encoder, HDMI_INFOFRAME_TYPE_AUDIO); + if (ret) + dev_err(dev, "Failed to stop audio infoframe: %d\n", ret); + + HD_WRITE(VC4_HD_MAI_CTL, VC4_HD_MAI_CTL_RESET); + HD_WRITE(VC4_HD_MAI_CTL, VC4_HD_MAI_CTL_ERRORF); + HD_WRITE(VC4_HD_MAI_CTL, VC4_HD_MAI_CTL_FLUSH); +} + +static void vc4_hdmi_audio_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct vc4_hdmi *hdmi = dai_to_hdmi(dai); + + if (substream != hdmi->audio.substream) + return; + + vc4_hdmi_audio_reset(hdmi); + + hdmi->audio.substream = NULL; +} + +/* HDMI audio codec callbacks */ +static int vc4_hdmi_audio_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct vc4_hdmi *hdmi = dai_to_hdmi(dai); + struct drm_encoder *encoder = hdmi->encoder; + struct drm_device *drm = encoder->dev; + struct device *dev = &hdmi->pdev->dev; + struct vc4_dev *vc4 = to_vc4_dev(drm); + u32 audio_packet_config, channel_mask; + u32 channel_map, i; + + if (substream != hdmi->audio.substream) + return -EINVAL; + + dev_dbg(dev, "%s: %u Hz, %d bit, %d channels\n", __func__, + params_rate(params), params_width(params), + params_channels(params)); + + hdmi->audio.channels = params_channels(params); + hdmi->audio.samplerate = params_rate(params); + + HD_WRITE(VC4_HD_MAI_CTL, + VC4_HD_MAI_CTL_RESET | + VC4_HD_MAI_CTL_FLUSH | + VC4_HD_MAI_CTL_DLATE | + VC4_HD_MAI_CTL_ERRORE | + VC4_HD_MAI_CTL_ERRORF); + + vc4_hdmi_audio_set_mai_clock(hdmi); + + audio_packet_config = + VC4_HDMI_AUDIO_PACKET_ZERO_DATA_ON_SAMPLE_FLAT | + VC4_HDMI_AUDIO_PACKET_ZERO_DATA_ON_INACTIVE_CHANNELS | + VC4_SET_FIELD(0xf, VC4_HDMI_AUDIO_PACKET_B_FRAME_IDENTIFIER); + + channel_mask = GENMASK(hdmi->audio.channels - 1, 0); + audio_packet_config |= VC4_SET_FIELD(channel_mask, + VC4_HDMI_AUDIO_PACKET_CEA_MASK); + + /* Set the MAI threshold. This logic mimics the firmware's. */ + if (hdmi->audio.samplerate > 96000) { + HD_WRITE(VC4_HD_MAI_THR, + VC4_SET_FIELD(0x12, VC4_HD_MAI_THR_DREQHIGH) | + VC4_SET_FIELD(0x12, VC4_HD_MAI_THR_DREQLOW)); + } else if (hdmi->audio.samplerate > 48000) { + HD_WRITE(VC4_HD_MAI_THR, + VC4_SET_FIELD(0x14, VC4_HD_MAI_THR_DREQHIGH) | + VC4_SET_FIELD(0x12, VC4_HD_MAI_THR_DREQLOW)); + } else { + HD_WRITE(VC4_HD_MAI_THR, + VC4_SET_FIELD(0x10, VC4_HD_MAI_THR_PANICHIGH) | + VC4_SET_FIELD(0x10, VC4_HD_MAI_THR_PANICLOW) | + VC4_SET_FIELD(0x10, VC4_HD_MAI_THR_DREQHIGH) | + VC4_SET_FIELD(0x10, VC4_HD_MAI_THR_DREQLOW)); + } + + HDMI_WRITE(VC4_HDMI_MAI_CONFIG, + VC4_HDMI_MAI_CONFIG_BIT_REVERSE | + VC4_SET_FIELD(channel_mask, VC4_HDMI_MAI_CHANNEL_MASK)); + + channel_map = 0; + for (i = 0; i < 8; i++) { + if (channel_mask & BIT(i)) + channel_map |= i << (3 * i); + } + + HDMI_WRITE(VC4_HDMI_MAI_CHANNEL_MAP, channel_map); + HDMI_WRITE(VC4_HDMI_AUDIO_PACKET_CONFIG, audio_packet_config); + vc4_hdmi_set_n_cts(hdmi); + + return 0; +} + +static int vc4_hdmi_audio_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ + struct vc4_hdmi *hdmi = dai_to_hdmi(dai); + struct drm_encoder *encoder = hdmi->encoder; + struct drm_device *drm = encoder->dev; + struct vc4_dev *vc4 = to_vc4_dev(drm); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + vc4_hdmi_set_audio_infoframe(encoder); + HDMI_WRITE(VC4_HDMI_TX_PHY_CTL0, + HDMI_READ(VC4_HDMI_TX_PHY_CTL0) & + ~VC4_HDMI_TX_PHY_RNG_PWRDN); + HD_WRITE(VC4_HD_MAI_CTL, + VC4_SET_FIELD(hdmi->audio.channels, + VC4_HD_MAI_CTL_CHNUM) | + VC4_HD_MAI_CTL_ENABLE); + break; + case SNDRV_PCM_TRIGGER_STOP: + HD_WRITE(VC4_HD_MAI_CTL, + VC4_HD_MAI_CTL_DLATE | + VC4_HD_MAI_CTL_ERRORE | + VC4_HD_MAI_CTL_ERRORF); + HDMI_WRITE(VC4_HDMI_TX_PHY_CTL0, + HDMI_READ(VC4_HDMI_TX_PHY_CTL0) | + VC4_HDMI_TX_PHY_RNG_PWRDN); + break; + default: + break; + } + + return 0; +} + +static inline struct vc4_hdmi * +snd_component_to_hdmi(struct snd_soc_component *component) +{ + struct snd_soc_card *card = snd_soc_component_get_drvdata(component); + + return snd_soc_card_get_drvdata(card); +} + +static int vc4_hdmi_audio_eld_ctl_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct vc4_hdmi *hdmi = snd_component_to_hdmi(component); + + uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES; + uinfo->count = sizeof(hdmi->connector->eld); + + return 0; +} + +static int vc4_hdmi_audio_eld_ctl_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct vc4_hdmi *hdmi = snd_component_to_hdmi(component); + + memcpy(ucontrol->value.bytes.data, hdmi->connector->eld, + sizeof(hdmi->connector->eld)); + + return 0; +} + +static const struct snd_kcontrol_new vc4_hdmi_audio_controls[] = { + { + .access = SNDRV_CTL_ELEM_ACCESS_READ | + SNDRV_CTL_ELEM_ACCESS_VOLATILE, + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "ELD", + .info = vc4_hdmi_audio_eld_ctl_info, + .get = vc4_hdmi_audio_eld_ctl_get, + }, +}; + +static const struct snd_soc_dapm_widget vc4_hdmi_audio_widgets[] = { + SND_SOC_DAPM_OUTPUT("TX"), +}; + +static const struct snd_soc_dapm_route vc4_hdmi_audio_routes[] = { + { "TX", NULL, "Playback" }, +}; + +static const struct snd_soc_codec_driver vc4_hdmi_audio_codec_drv = { + .component_driver = { + .controls = vc4_hdmi_audio_controls, + .num_controls = ARRAY_SIZE(vc4_hdmi_audio_controls), + .dapm_widgets = vc4_hdmi_audio_widgets, + .num_dapm_widgets = ARRAY_SIZE(vc4_hdmi_audio_widgets), + .dapm_routes = vc4_hdmi_audio_routes, + .num_dapm_routes = ARRAY_SIZE(vc4_hdmi_audio_routes), + }, +}; + +static const struct snd_soc_dai_ops vc4_hdmi_audio_dai_ops = { + .startup = vc4_hdmi_audio_startup, + .shutdown = vc4_hdmi_audio_shutdown, + .hw_params = vc4_hdmi_audio_hw_params, + .set_fmt = vc4_hdmi_audio_set_fmt, + .trigger = vc4_hdmi_audio_trigger, +}; + +static struct snd_soc_dai_driver vc4_hdmi_audio_codec_dai_drv = { + .name = "vc4-hdmi-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 2, + .channels_max = 8, + .rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 | + SNDRV_PCM_RATE_192000, + .formats = SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE, + }, +}; + +static const struct snd_soc_component_driver vc4_hdmi_audio_cpu_dai_comp = { + .name = "vc4-hdmi-cpu-dai-component", +}; + +static int vc4_hdmi_audio_cpu_dai_probe(struct snd_soc_dai *dai) +{ + struct vc4_hdmi *hdmi = dai_to_hdmi(dai); + + snd_soc_dai_init_dma_data(dai, &hdmi->audio.dma_data, NULL); + + return 0; +} + +static struct snd_soc_dai_driver vc4_hdmi_audio_cpu_dai_drv = { + .name = "vc4-hdmi-cpu-dai", + .probe = vc4_hdmi_audio_cpu_dai_probe, + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 8, + .rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 | + SNDRV_PCM_RATE_192000, + .formats = SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE, + }, + .ops = &vc4_hdmi_audio_dai_ops, +}; + +static const struct snd_dmaengine_pcm_config pcm_conf = { + .chan_names[SNDRV_PCM_STREAM_PLAYBACK] = "audio-rx", + .prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config, +}; + +static int vc4_hdmi_audio_init(struct vc4_hdmi *hdmi) +{ + struct snd_soc_dai_link *dai_link = &hdmi->audio.link; + struct snd_soc_card *card = &hdmi->audio.card; + struct device *dev = &hdmi->pdev->dev; + const __be32 *addr; + int ret; + + if (!of_find_property(dev->of_node, "dmas", NULL)) { + dev_warn(dev, + "'dmas' DT property is missing, no HDMI audio\n"); + return 0; + } + + /* + * Get the physical address of VC4_HD_MAI_DATA. We need to retrieve + * the bus address specified in the DT, because the physical address + * (the one returned by platform_get_resource()) is not appropriate + * for DMA transfers. + * This VC/MMU should probably be exposed to avoid this kind of hacks. + */ + addr = of_get_address(dev->of_node, 1, NULL, NULL); + hdmi->audio.dma_data.addr = be32_to_cpup(addr) + VC4_HD_MAI_DATA; + hdmi->audio.dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + hdmi->audio.dma_data.maxburst = 2; + + ret = devm_snd_dmaengine_pcm_register(dev, &pcm_conf, 0); + if (ret) { + dev_err(dev, "Could not register PCM component: %d\n", ret); + return ret; + } + + ret = devm_snd_soc_register_component(dev, &vc4_hdmi_audio_cpu_dai_comp, + &vc4_hdmi_audio_cpu_dai_drv, 1); + if (ret) { + dev_err(dev, "Could not register CPU DAI: %d\n", ret); + return ret; + } + + /* register codec and codec dai */ + ret = snd_soc_register_codec(dev, &vc4_hdmi_audio_codec_drv, + &vc4_hdmi_audio_codec_dai_drv, 1); + if (ret) { + dev_err(dev, "Could not register codec: %d\n", ret); + return ret; + } + + dai_link->name = "MAI"; + dai_link->stream_name = "MAI PCM"; + dai_link->codec_dai_name = vc4_hdmi_audio_codec_dai_drv.name; + dai_link->cpu_dai_name = dev_name(dev); + dai_link->codec_name = dev_name(dev); + dai_link->platform_name = dev_name(dev); + + card->dai_link = dai_link; + card->num_links = 1; + card->name = "vc4-hdmi"; + card->dev = dev; + + /* + * Be careful, snd_soc_register_card() calls dev_set_drvdata() and + * stores a pointer to the snd card object in dev->driver_data. This + * means we cannot use it for something else. The hdmi back-pointer is + * now stored in card->drvdata and should be retrieved with + * snd_soc_card_get_drvdata() if needed. + */ + snd_soc_card_set_drvdata(card, hdmi); + ret = devm_snd_soc_register_card(dev, card); + if (ret) { + dev_err(dev, "Could not register sound card: %d\n", ret); + goto unregister_codec; + } + + return 0; + +unregister_codec: + snd_soc_unregister_codec(dev); + + return ret; +} + +static void vc4_hdmi_audio_cleanup(struct vc4_hdmi *hdmi) +{ + struct device *dev = &hdmi->pdev->dev; + + /* + * If drvdata is not set this means the audio card was not + * registered, just skip codec unregistration in this case. + */ + if (dev_get_drvdata(dev)) + snd_soc_unregister_codec(dev); +} + static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data) { struct platform_device *pdev = to_platform_device(dev); @@ -737,6 +1223,10 @@ static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data) goto err_destroy_encoder; } + ret = vc4_hdmi_audio_init(hdmi); + if (ret) + goto err_destroy_encoder; + return 0; err_destroy_encoder: @@ -758,6 +1248,8 @@ static void vc4_hdmi_unbind(struct device *dev, struct device *master, struct vc4_dev *vc4 = drm->dev_private; struct vc4_hdmi *hdmi = vc4->hdmi; + vc4_hdmi_audio_cleanup(hdmi); + vc4_hdmi_connector_destroy(hdmi->connector); vc4_hdmi_encoder_destroy(hdmi->encoder); diff --git a/drivers/gpu/drm/vc4/vc4_plane.c b/drivers/gpu/drm/vc4/vc4_plane.c index 110224c3a3ac..0f4564beb017 100644 --- a/drivers/gpu/drm/vc4/vc4_plane.c +++ b/drivers/gpu/drm/vc4/vc4_plane.c @@ -842,10 +842,8 @@ struct drm_plane *vc4_plane_init(struct drm_device *dev, vc4_plane = devm_kzalloc(dev->dev, sizeof(*vc4_plane), GFP_KERNEL); - if (!vc4_plane) { - ret = -ENOMEM; - goto fail; - } + if (!vc4_plane) + return ERR_PTR(-ENOMEM); for (i = 0; i < ARRAY_SIZE(hvs_formats); i++) { /* Don't allow YUV in cursor planes, since that means @@ -866,9 +864,4 @@ struct drm_plane *vc4_plane_init(struct drm_device *dev, drm_plane_helper_add(plane, &vc4_plane_helper_funcs); return plane; -fail: - if (plane) - vc4_plane_destroy(plane); - - return ERR_PTR(ret); } diff --git a/drivers/gpu/drm/vc4/vc4_regs.h b/drivers/gpu/drm/vc4/vc4_regs.h index 385405a2df05..932093936178 100644 --- a/drivers/gpu/drm/vc4/vc4_regs.h +++ b/drivers/gpu/drm/vc4/vc4_regs.h @@ -446,11 +446,62 @@ #define VC4_HDMI_HOTPLUG 0x00c # define VC4_HDMI_HOTPLUG_CONNECTED BIT(0) +/* 3 bits per field, where each field maps from that corresponding MAI + * bus channel to the given HDMI channel. + */ +#define VC4_HDMI_MAI_CHANNEL_MAP 0x090 + +#define VC4_HDMI_MAI_CONFIG 0x094 +# define VC4_HDMI_MAI_CONFIG_FORMAT_REVERSE BIT(27) +# define VC4_HDMI_MAI_CONFIG_BIT_REVERSE BIT(26) +# define VC4_HDMI_MAI_CHANNEL_MASK_MASK VC4_MASK(15, 0) +# define VC4_HDMI_MAI_CHANNEL_MASK_SHIFT 0 + +/* Last received format word on the MAI bus. */ +#define VC4_HDMI_MAI_FORMAT 0x098 + +#define VC4_HDMI_AUDIO_PACKET_CONFIG 0x09c +# define VC4_HDMI_AUDIO_PACKET_ZERO_DATA_ON_SAMPLE_FLAT BIT(29) +# define VC4_HDMI_AUDIO_PACKET_ZERO_DATA_ON_INACTIVE_CHANNELS BIT(24) +# define VC4_HDMI_AUDIO_PACKET_FORCE_SAMPLE_PRESENT BIT(19) +# define VC4_HDMI_AUDIO_PACKET_FORCE_B_FRAME BIT(18) +# define VC4_HDMI_AUDIO_PACKET_B_FRAME_IDENTIFIER_MASK VC4_MASK(13, 10) +# define VC4_HDMI_AUDIO_PACKET_B_FRAME_IDENTIFIER_SHIFT 10 +/* If set, then multichannel, otherwise 2 channel. */ +# define VC4_HDMI_AUDIO_PACKET_AUDIO_LAYOUT BIT(9) +/* If set, then AUDIO_LAYOUT overrides audio_cea_mask */ +# define VC4_HDMI_AUDIO_PACKET_FORCE_AUDIO_LAYOUT BIT(8) +# define VC4_HDMI_AUDIO_PACKET_CEA_MASK_MASK VC4_MASK(7, 0) +# define VC4_HDMI_AUDIO_PACKET_CEA_MASK_SHIFT 0 + #define VC4_HDMI_RAM_PACKET_CONFIG 0x0a0 # define VC4_HDMI_RAM_PACKET_ENABLE BIT(16) #define VC4_HDMI_RAM_PACKET_STATUS 0x0a4 +#define VC4_HDMI_CRP_CFG 0x0a8 +/* When set, the CTS_PERIOD counts based on MAI bus sync pulse instead + * of pixel clock. + */ +# define VC4_HDMI_CRP_USE_MAI_BUS_SYNC_FOR_CTS BIT(26) +/* When set, no CRP packets will be sent. */ +# define VC4_HDMI_CRP_CFG_DISABLE BIT(25) +/* If set, generates CTS values based on N, audio clock, and video + * clock. N must be divisible by 128. + */ +# define VC4_HDMI_CRP_CFG_EXTERNAL_CTS_EN BIT(24) +# define VC4_HDMI_CRP_CFG_N_MASK VC4_MASK(19, 0) +# define VC4_HDMI_CRP_CFG_N_SHIFT 0 + +/* 20-bit fields containing CTS values to be transmitted if !EXTERNAL_CTS_EN */ +#define VC4_HDMI_CTS_0 0x0ac +#define VC4_HDMI_CTS_1 0x0b0 +/* 20-bit fields containing number of clocks to send CTS0/1 before + * switching to the other one. + */ +#define VC4_HDMI_CTS_PERIOD_0 0x0b4 +#define VC4_HDMI_CTS_PERIOD_1 0x0b8 + #define VC4_HDMI_HORZA 0x0c4 # define VC4_HDMI_HORZA_VPOS BIT(14) # define VC4_HDMI_HORZA_HPOS BIT(13) @@ -512,7 +563,11 @@ #define VC4_HDMI_TX_PHY_RESET_CTL 0x2c0 -#define VC4_HDMI_GCP_0 0x400 +#define VC4_HDMI_TX_PHY_CTL0 0x2c4 +# define VC4_HDMI_TX_PHY_RNG_PWRDN BIT(25) + +#define VC4_HDMI_GCP(x) (0x400 + ((x) * 0x4)) +#define VC4_HDMI_RAM_PACKET(x) (0x400 + ((x) * 0x24)) #define VC4_HDMI_PACKET_STRIDE 0x24 #define VC4_HD_M_CTL 0x00c @@ -522,6 +577,56 @@ # define VC4_HD_M_ENABLE BIT(0) #define VC4_HD_MAI_CTL 0x014 +/* Set when audio stream is received at a slower rate than the + * sampling period, so MAI fifo goes empty. Write 1 to clear. + */ +# define VC4_HD_MAI_CTL_DLATE BIT(15) +# define VC4_HD_MAI_CTL_BUSY BIT(14) +# define VC4_HD_MAI_CTL_CHALIGN BIT(13) +# define VC4_HD_MAI_CTL_WHOLSMP BIT(12) +# define VC4_HD_MAI_CTL_FULL BIT(11) +# define VC4_HD_MAI_CTL_EMPTY BIT(10) +# define VC4_HD_MAI_CTL_FLUSH BIT(9) +/* If set, MAI bus generates SPDIF (bit 31) parity instead of passing + * through. + */ +# define VC4_HD_MAI_CTL_PAREN BIT(8) +# define VC4_HD_MAI_CTL_CHNUM_MASK VC4_MASK(7, 4) +# define VC4_HD_MAI_CTL_CHNUM_SHIFT 4 +# define VC4_HD_MAI_CTL_ENABLE BIT(3) +/* Underflow error status bit, write 1 to clear. */ +# define VC4_HD_MAI_CTL_ERRORE BIT(2) +/* Overflow error status bit, write 1 to clear. */ +# define VC4_HD_MAI_CTL_ERRORF BIT(1) +/* Single-shot reset bit. Read value is undefined. */ +# define VC4_HD_MAI_CTL_RESET BIT(0) + +#define VC4_HD_MAI_THR 0x018 +# define VC4_HD_MAI_THR_PANICHIGH_MASK VC4_MASK(29, 24) +# define VC4_HD_MAI_THR_PANICHIGH_SHIFT 24 +# define VC4_HD_MAI_THR_PANICLOW_MASK VC4_MASK(21, 16) +# define VC4_HD_MAI_THR_PANICLOW_SHIFT 16 +# define VC4_HD_MAI_THR_DREQHIGH_MASK VC4_MASK(13, 8) +# define VC4_HD_MAI_THR_DREQHIGH_SHIFT 8 +# define VC4_HD_MAI_THR_DREQLOW_MASK VC4_MASK(5, 0) +# define VC4_HD_MAI_THR_DREQLOW_SHIFT 0 + +/* Format header to be placed on the MAI data. Unused. */ +#define VC4_HD_MAI_FMT 0x01c + +/* Register for DMAing in audio data to be transported over the MAI + * bus to the Falcon core. + */ +#define VC4_HD_MAI_DATA 0x020 + +/* Divider from HDMI HSM clock to MAI serial clock. Sampling period + * converges to N / (M + 1) cycles. + */ +#define VC4_HD_MAI_SMP 0x02c +# define VC4_HD_MAI_SMP_N_MASK VC4_MASK(31, 8) +# define VC4_HD_MAI_SMP_N_SHIFT 8 +# define VC4_HD_MAI_SMP_M_MASK VC4_MASK(7, 0) +# define VC4_HD_MAI_SMP_M_SHIFT 0 #define VC4_HD_VID_CTL 0x038 # define VC4_HD_VID_CTL_ENABLE BIT(31) diff --git a/drivers/gpu/drm/vgem/vgem_drv.c b/drivers/gpu/drm/vgem/vgem_drv.c index a1f42d125e6e..9fee38a942c4 100644 --- a/drivers/gpu/drm/vgem/vgem_drv.c +++ b/drivers/gpu/drm/vgem/vgem_drv.c @@ -104,7 +104,7 @@ static int vgem_open(struct drm_device *dev, struct drm_file *file) return 0; } -static void vgem_preclose(struct drm_device *dev, struct drm_file *file) +static void vgem_postclose(struct drm_device *dev, struct drm_file *file) { struct vgem_file *vfile = file->driver_priv; @@ -303,7 +303,7 @@ static int vgem_prime_mmap(struct drm_gem_object *obj, static struct drm_driver vgem_driver = { .driver_features = DRIVER_GEM | DRIVER_PRIME, .open = vgem_open, - .preclose = vgem_preclose, + .postclose = vgem_postclose, .gem_free_object_unlocked = vgem_gem_free_object, .gem_vm_ops = &vgem_gem_vm_ops, .ioctls = vgem_ioctls, diff --git a/drivers/gpu/drm/virtio/virtgpu_vq.c b/drivers/gpu/drm/virtio/virtgpu_vq.c index 472e34986a44..9eb96fb2c147 100644 --- a/drivers/gpu/drm/virtio/virtgpu_vq.c +++ b/drivers/gpu/drm/virtio/virtgpu_vq.c @@ -97,8 +97,8 @@ virtio_gpu_get_vbuf(struct virtio_gpu_device *vgdev, struct virtio_gpu_vbuffer *vbuf; vbuf = kmem_cache_alloc(vgdev->vbufs, GFP_KERNEL); - if (IS_ERR(vbuf)) - return ERR_CAST(vbuf); + if (!vbuf) + return ERR_PTR(-ENOMEM); memset(vbuf, 0, VBUFFER_SIZE); BUG_ON(size > MAX_INLINE_CMD_SIZE); diff --git a/drivers/gpu/drm/zte/zx_drm_drv.c b/drivers/gpu/drm/zte/zx_drm_drv.c index b24a70ba4b83..614e854f6be5 100644 --- a/drivers/gpu/drm/zte/zx_drm_drv.c +++ b/drivers/gpu/drm/zte/zx_drm_drv.c @@ -53,19 +53,7 @@ static void zx_drm_lastclose(struct drm_device *drm) drm_fbdev_cma_restore_mode(priv->fbdev); } -static const struct file_operations zx_drm_fops = { - .owner = THIS_MODULE, - .open = drm_open, - .release = drm_release, - .unlocked_ioctl = drm_ioctl, -#ifdef CONFIG_COMPAT - .compat_ioctl = drm_compat_ioctl, -#endif - .poll = drm_poll, - .read = drm_read, - .llseek = noop_llseek, - .mmap = drm_gem_cma_mmap, -}; +DEFINE_DRM_GEM_CMA_FOPS(zx_drm_fops); static struct drm_driver zx_drm_driver = { .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME | |