drm/msm: A DRM/KMS driver for snapdragon SoCs
Subsystems (4)
cover drm/msm: A DRM/KMS driver for snapdragon SoCs
This patchset can also be found here, for easier browsing:
https://github.com/freedreno/kernel-msm/commits/ifc6410-drm-rfc1
git://github.com/freedreno/kernel-msm.git ifc6410-drm-rfc1
NOTES about msm drm/kms driver:
In the current snapdragon SoC's, we have (at least) 3 different display controller blocks at play:
+ MDP3 - ?? seems to be what is on geeksphone peak device
+ MDP4 - S3 (APQ8060, touchpad), S4-pro (APQ8064, nexus4 & ifc6410)
+ MDSS - snapdragon 800
(I don't have a completely clear picture on which display controller maps to which part #)
Plus a handful of blocks around them for HDMI/DSI/etc output.
And on gpu side of things:
+ zero, one, or two 2d cores (z180)
+ and either a2xx or a3xx 3d core.
But, HDMI/DSI/etc blocks seem like they can be shared across multiple display controller blocks. And I for sure don't want to have to deal with N different kms devices from xf86-video-freedreno. Plus, it seems like we can do some clever tricks like use GPU to trigger pageflip after rendering completes (ie. have the kms/crtc code build up gpu cmdstream to update scanout and write FLUSH register after).
So, the approach is one drm driver, with some modularity. Different 'struct msm_kms' implementations, depending on display controller. And one or more 'struct msm_gpu' for the various different gpu sub- modules.
(Second part is not implemented yet. So far this is just basic KMS driver, and not exposing any custom ioctls to userspace for now.)
The kms module provides the plane, crtc, and encoder objects, and loads whatever connectors are appropriate.
For MDP4, the mapping is:
plane -> PIPE{RGBn,VGn} \
crtc -> OVLP{n} + DMA{P,S,E} (??) |-> MDP "device"
encoder -> DTV/LCDC/DSI (within MDP4) /
connector -> HDMI/DSI/etc --> other device(s)Since the irq's that drm core mostly cares about are vblank/framedone, we'll let msm_mdp4_kms provide the irq install/uninstall/etc functions and treat the MDP4 block's irq as "the" irq. Even though the connectors may have their own irqs which they install themselves. For this reason the display controller is the "master" device.
Each connector probably ends up being a separate device, just for the logistics of finding/mapping io region, irq, etc. Idealy we would have a better way than just stashing the platform device in a global (ie. like DT super-node.. but I don't have any snapdragon hw yet that is using DT).
Note that so far I've not been able to get any docs on the hw, and it seems that access to such docs would prevent me from working on the freedreno gallium driver. So there may be some mistakes in register names (I had to invent a few, since no sufficient hint was given in the downstream android fbdev driver), bitfield sizes, etc. My current state of understanding the registers is given in the envytools rnndb files at:
https://github.com/freedreno/envytools/tree/master/rnndb
(the mdp4/hdmi/dsi directories)
These files are used both for a parser tool (in the same tree) to parse logged register reads/writes (both from downstream android fbdev driver, and this driver with register logging enabled), as well as to generate the register level headers.
Rob Clark (2):
drm/msm: add register definitions
drm/msm: basic KMS driver for snapdragon
drivers/gpu/drm/Kconfig | 2 + drivers/gpu/drm/Makefile | 1 + drivers/gpu/drm/msm/Kconfig | 34 + drivers/gpu/drm/msm/Makefile | 23 + drivers/gpu/drm/msm/NOTES | 43 ++ drivers/gpu/drm/msm/dsi/dsi.xml.h | 262 +++++++ drivers/gpu/drm/msm/dsi/mmss_cc.xml.h | 156 +++++ drivers/gpu/drm/msm/dsi/sfpb.xml.h | 46 ++ drivers/gpu/drm/msm/hdmi/hdmi.xml.h | 508 ++++++++++++++ drivers/gpu/drm/msm/hdmi/hdmi_connector.c | 528 ++++++++++++++ drivers/gpu/drm/msm/hdmi/hdmi_connector.h | 95 +++ drivers/gpu/drm/msm/hdmi/hdmi_i2c.c | 264 +++++++ drivers/gpu/drm/msm/hdmi/hdmi_phy_8960.c | 140 ++++ drivers/gpu/drm/msm/hdmi/hdmi_phy_8x60.c | 215 ++++++ drivers/gpu/drm/msm/hdmi/qfprom.xml.h | 50 ++ drivers/gpu/drm/msm/mdp4/mdp4.xml.h | 1001 +++++++++++++++++++++++++++ drivers/gpu/drm/msm/mdp4/mdp4_crtc.c | 440 ++++++++++++ drivers/gpu/drm/msm/mdp4/mdp4_dtv_encoder.c | 306 ++++++++ drivers/gpu/drm/msm/mdp4/mdp4_irq.c | 194 ++++++ drivers/gpu/drm/msm/mdp4/mdp4_kms.c | 359 ++++++++++ drivers/gpu/drm/msm/mdp4/mdp4_kms.h | 161 +++++ drivers/gpu/drm/msm/mdp4/mdp4_plane.c | 241 +++++++ drivers/gpu/drm/msm/msm_connector.c | 34 + drivers/gpu/drm/msm/msm_connector.h | 68 ++ drivers/gpu/drm/msm/msm_drv.c | 491 +++++++++++++ drivers/gpu/drm/msm/msm_drv.h | 161 +++++ drivers/gpu/drm/msm/msm_fb.c | 216 ++++++ drivers/gpu/drm/msm/msm_fbdev.c | 255 +++++++ drivers/gpu/drm/msm/msm_gem.c | 441 ++++++++++++ 29 files changed, 6735 insertions(+) create mode 100644 drivers/gpu/drm/msm/Kconfig create mode 100644 drivers/gpu/drm/msm/Makefile create mode 100644 drivers/gpu/drm/msm/NOTES create mode 100644 drivers/gpu/drm/msm/dsi/dsi.xml.h create mode 100644 drivers/gpu/drm/msm/dsi/mmss_cc.xml.h create mode 100644 drivers/gpu/drm/msm/dsi/sfpb.xml.h create mode 100644 drivers/gpu/drm/msm/hdmi/hdmi.xml.h create mode 100644 drivers/gpu/drm/msm/hdmi/hdmi_connector.c create mode 100644 drivers/gpu/drm/msm/hdmi/hdmi_connector.h create mode 100644 drivers/gpu/drm/msm/hdmi/hdmi_i2c.c create mode 100644 drivers/gpu/drm/msm/hdmi/hdmi_phy_8960.c create mode 100644 drivers/gpu/drm/msm/hdmi/hdmi_phy_8x60.c create mode 100644 drivers/gpu/drm/msm/hdmi/qfprom.xml.h create mode 100644 drivers/gpu/drm/msm/mdp4/mdp4.xml.h create mode 100644 drivers/gpu/drm/msm/mdp4/mdp4_crtc.c create mode 100644 drivers/gpu/drm/msm/mdp4/mdp4_dtv_encoder.c create mode 100644 drivers/gpu/drm/msm/mdp4/mdp4_irq.c create mode 100644 drivers/gpu/drm/msm/mdp4/mdp4_kms.c create mode 100644 drivers/gpu/drm/msm/mdp4/mdp4_kms.h create mode 100644 drivers/gpu/drm/msm/mdp4/mdp4_plane.c create mode 100644 drivers/gpu/drm/msm/msm_connector.c create mode 100644 drivers/gpu/drm/msm/msm_connector.h create mode 100644 drivers/gpu/drm/msm/msm_drv.c create mode 100644 drivers/gpu/drm/msm/msm_drv.h create mode 100644 drivers/gpu/drm/msm/msm_fb.c create mode 100644 drivers/gpu/drm/msm/msm_fbdev.c create mode 100644 drivers/gpu/drm/msm/msm_gem.c
Signature · 1 line
1.8.1.4
RFC 1/2 drm/msm: add register definitions
Generated from rnndb files in:
https://github.com/freedreno/envytools
Keep this split out as a separate commit to make it easier to review the actual driver.
--- drivers/gpu/drm/msm/dsi/dsi.xml.h | 262 +++++++++ drivers/gpu/drm/msm/dsi/mmss_cc.xml.h | 156 +++++ drivers/gpu/drm/msm/dsi/sfpb.xml.h | 46 ++ drivers/gpu/drm/msm/hdmi/hdmi.xml.h | 508 +++++++++++++++++ drivers/gpu/drm/msm/hdmi/qfprom.xml.h | 50 ++ drivers/gpu/drm/msm/mdp4/mdp4.xml.h | 1001 +++++++++++++++++++++++++++++++++ 6 files changed, 2023 insertions(+) create mode 100644 drivers/gpu/drm/msm/dsi/dsi.xml.h create mode 100644 drivers/gpu/drm/msm/dsi/mmss_cc.xml.h create mode 100644 drivers/gpu/drm/msm/dsi/sfpb.xml.h create mode 100644 drivers/gpu/drm/msm/hdmi/hdmi.xml.h create mode 100644 drivers/gpu/drm/msm/hdmi/qfprom.xml.h create mode 100644 drivers/gpu/drm/msm/mdp4/mdp4.xml.h
drivers/gpu/drm/msm/dsi/dsi.xml.h new file +262 -0
@@ -0,0 +1,262 @@ +#ifndef DSI_XML +#define DSI_XML + +/* Autogenerated file, DO NOT EDIT manually! + +This file was generated by the rules-ng-ng headergen tool in this git repository: +http://0x04.net/cgit/index.cgi/rules-ng-ng +git clone git://0x04.net/rules-ng-ng + +The rules-ng-ng source files this header was generated from are: +- /home/robclark/src/freedreno/envytools/rnndb/msm.xml ( 595 bytes, from 2013-07-05 19:21:12) +- /home/robclark/src/freedreno/envytools/rnndb/freedreno_copyright.xml ( 1453 bytes, from 2013-03-31 16:51:27) +- /home/robclark/src/freedreno/envytools/rnndb/mdp4/mdp4.xml ( 18030 bytes, from 2013-07-05 19:21:12) +- /home/robclark/src/freedreno/envytools/rnndb/dsi/dsi.xml ( 6265 bytes, from 2013-07-05 19:21:12) +- /home/robclark/src/freedreno/envytools/rnndb/dsi/sfpb.xml ( 307 bytes, from 2013-07-05 19:21:12) +- /home/robclark/src/freedreno/envytools/rnndb/dsi/mmss_cc.xml ( 1622 bytes, from 2013-07-05 19:21:12) +- /home/robclark/src/freedreno/envytools/rnndb/hdmi/qfprom.xml ( 600 bytes, from 2013-07-05 19:21:12) +- /home/robclark/src/freedreno/envytools/rnndb/hdmi/hdmi.xml ( 19288 bytes, from 2013-07-05 19:21:12) + +Copyright (C) 2013 by the following authors: +- Rob Clark <[email protected]> (robclark) + +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, sublicense, 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 NONINFRINGEMENT. +IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS 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. +*/ + + +#define DSI_IRQ_CMD_DMA_DONE 0x00000001 +#define DSI_IRQ_MASK_CMD_DMA_DONE 0x00000002 +#define DSI_IRQ_CMD_MDP_DONE 0x00000100 +#define DSI_IRQ_MASK_CMD_MDP_DONE 0x00000200 +#define DSI_IRQ_VIDEO_DONE 0x00010000 +#define DSI_IRQ_MASK_VIDEO_DONE 0x00020000 +#define DSI_IRQ_ERROR 0x01000000 +#define DSI_IRQ_MASK_ERROR 0x02000000 +#define REG_DSI_CNTL 0x00000000 +#define DSI_CNTL_ENABLE 0x00000001 +#define DSI_CNTL_VID_MODE_EN 0x00000002 +#define DSI_CNTL_CMD_MODE_EN 0x00000004 + +#define REG_DSI_STATUS0 0x00000004 +#define DSI_STATUS0_CMD_MODE_DMA_BUSY 0x00000002 +#define DSI_STATUS0_VIDEO_MODE_ENGINE_BUSY 0x00000008 +#define DSI_STATUS0_DSI_BUSY 0x00000010 + +#define REG_DSI_FIFO_STATUS 0x00000008 + +#define REG_DSI_VID_CFG0 0x0000000c + +#define REG_DSI_VID_CFG1 0x0000001c + +#define REG_DSI_CMD_DMA_CTRL 0x00000038 + +#define REG_DSI_CMD_CFG0 0x0000003c + +#define REG_DSI_CMD_CFG1 0x00000040 + +#define REG_DSI_DMA_BASE 0x00000044 + +#define REG_DSI_DMA_LEN 0x00000048 + +#define REG_DSI_ACK_ERR_STATUS 0x00000064 + +static inline uint32_t REG_DSI_RDBK (uint32_t i0) {return 0x00000068 +0x4 *i0; } + +static inline uint32_t REG_DSI_RDBK_DATA (uint32_t i0) {return 0x00000068 +0x4 *i0; } + +#define REG_DSI_TRIG_CTRL 0x00000080 + +#define REG_DSI_TRIG_DMA 0x0000008c + +#define REG_DSI_DLN0_PHY_ERR 0x000000b0 + +#define REG_DSI_TIMEOUT_STATUS 0x000000bc + +#define REG_DSI_CLKOUT_TIMING_CTRL 0x000000c0 + +#define REG_DSI_EOT_PACKET_CTRL 0x000000c8 + +#define REG_DSI_LAN_SWAP_CTRL 0x000000ac + +#define REG_DSI_ERR_INT_MASK0 0x00000108 + +#define REG_DSI_INTR_CTRL 0x0000010c + +#define REG_DSI_RESET 0x00000114 + +#define REG_DSI_CLK_CTRL 0x00000118 + +#define REG_DSI_PHY_RESET 0x00000128 + +#define REG_DSI_PHY_PLL_CTRL_0 0x00000200 + +#define REG_DSI_PHY_PLL_CTRL_1 0x00000204 + +#define REG_DSI_PHY_PLL_CTRL_2 0x00000208 + +#define REG_DSI_PHY_PLL_CTRL_3 0x0000020c + +#define REG_DSI_PHY_PLL_CTRL_4 0x00000210 + +#define REG_DSI_PHY_PLL_CTRL_5 0x00000214 + +#define REG_DSI_PHY_PLL_CTRL_6 0x00000218 + +#define REG_DSI_PHY_PLL_CTRL_7 0x0000021c + +#define REG_DSI_PHY_PLL_CTRL_8 0x00000220 + +#define REG_DSI_PHY_PLL_CTRL_9 0x00000224 + +#define REG_DSI_PHY_PLL_CTRL_10 0x00000228 + +#define REG_DSI_PHY_PLL_CTRL_11 0x0000022c + +#define REG_DSI_PHY_PLL_CTRL_12 0x00000230 + +#define REG_DSI_PHY_PLL_CTRL_13 0x00000234 + +#define REG_DSI_PHY_PLL_CTRL_14 0x00000238 + +#define REG_DSI_PHY_PLL_CTRL_15 0x0000023c + +#define REG_DSI_PHY_PLL_CTRL_16 0x00000240 + +#define REG_DSI_PHY_PLL_CTRL_17 0x00000244 + +#define REG_DSI_PHY_PLL_CTRL_18 0x00000248 + +#define REG_DSI_PHY_PLL_CTRL_19 0x0000024c + +#define REG_DSI_PHY_PLL_STATUS 0x00000280 +#define DSI_PHY_PLL_STATUS_PLL_BUSY 0x00000001 + +static inline uint32_t REG_DSI_LN (uint32_t i0) {return 0x00000300 +0x40 *i0; } + +static inline uint32_t REG_DSI_LN_CFG_0 (uint32_t i0) {return 0x00000300 +0x40 *i0; } + +static inline uint32_t REG_DSI_LN_CFG_1 (uint32_t i0) {return 0x00000304 +0x40 *i0; } + +static inline uint32_t REG_DSI_LN_CFG_2 (uint32_t i0) {return 0x00000308 +0x40 *i0; } + +static inline uint32_t REG_DSI_LN_TEST_DATAPATH (uint32_t i0) {return 0x0000030c +0x40 *i0; } + +static inline uint32_t REG_DSI_LN_TEST_STR_0 (uint32_t i0) {return 0x00000314 +0x40 *i0; } + +static inline uint32_t REG_DSI_LN_TEST_STR_1 (uint32_t i0) {return 0x00000318 +0x40 *i0; } + +#define REG_DSI_PHY_LNCK_CFG_0 0x00000400 + +#define REG_DSI_PHY_LNCK_CFG_1 0x00000404 + +#define REG_DSI_PHY_LNCK_CFG_2 0x00000408 + +#define REG_DSI_PHY_LNCK_TEST_DATAPATH 0x0000040c + +#define REG_DSI_PHY_LNCK_TEST_STR0 0x00000414 + +#define REG_DSI_PHY_LNCK_TEST_STR1 0x00000418 + +#define REG_DSI_PHY_TIMING_CTRL_0 0x00000440 + +#define REG_DSI_PHY_TIMING_CTRL_1 0x00000444 + +#define REG_DSI_PHY_TIMING_CTRL_2 0x00000448 + +#define REG_DSI_PHY_TIMING_CTRL_3 0x0000044c + +#define REG_DSI_PHY_TIMING_CTRL_4 0x00000450 + +#define REG_DSI_PHY_TIMING_CTRL_5 0x00000454 + +#define REG_DSI_PHY_TIMING_CTRL_6 0x00000458 + +#define REG_DSI_PHY_TIMING_CTRL_7 0x0000045c + +#define REG_DSI_PHY_TIMING_CTRL_8 0x00000460 + +#define REG_DSI_PHY_TIMING_CTRL_9 0x00000464 + +#define REG_DSI_PHY_TIMING_CTRL_10 0x00000468 + +#define REG_DSI_PHY_TIMING_CTRL_11 0x0000046c + +#define REG_DSI_PHY_CTRL_0 0x00000470 + +#define REG_DSI_PHY_CTRL_1 0x00000474 + +#define REG_DSI_PHY_CTRL_2 0x00000478 + +#define REG_DSI_PHY_CTRL_3 0x0000047c + +#define REG_DSI_PHY_STRENGTH_0 0x00000480 + +#define REG_DSI_PHY_STRENGTH_1 0x00000484 + +#define REG_DSI_PHY_STRENGTH_2 0x00000488 + +#define REG_DSI_PHY_BIST_CTRL_0 0x0000048c + +#define REG_DSI_PHY_BIST_CTRL_1 0x00000490 + +#define REG_DSI_PHY_BIST_CTRL_2 0x00000494 + +#define REG_DSI_PHY_BIST_CTRL_3 0x00000498 + +#define REG_DSI_PHY_BIST_CTRL_4 0x0000049c + +#define REG_DSI_PHY_LDO_CNTL 0x000004b0 + +#define REG_DSI_PHY_REGULATOR_CTRL_0 0x00000500 + +#define REG_DSI_PHY_REGULATOR_CTRL_1 0x00000504 + +#define REG_DSI_PHY_REGULATOR_CTRL_2 0x00000508 + +#define REG_DSI_PHY_REGULATOR_CTRL_3 0x0000050c + +#define REG_DSI_PHY_REGULATOR_CTRL_4 0x00000510 + +#define REG_DSI_PHY_REGULATOR_CAL_PWR_CFG 0x00000518 + +#define REG_DSI_PHY_CAL_HW_TRIGGER 0x00000528 + +#define REG_DSI_PHY_CAL_SW_CFG_0 0x0000052c + +#define REG_DSI_PHY_CAL_SW_CFG_1 0x00000530 + +#define REG_DSI_PHY_CAL_SW_CFG_2 0x00000534 + +#define REG_DSI_PHY_CAL_HW_CFG_0 0x00000538 + +#define REG_DSI_PHY_CAL_HW_CFG_1 0x0000053c + +#define REG_DSI_PHY_CAL_HW_CFG_2 0x00000540 + +#define REG_DSI_PHY_CAL_HW_CFG_3 0x00000544 + +#define REG_DSI_PHY_CAL_HW_CFG_4 0x00000548 + +#define REG_DSI_PHY_CAL_STATUS 0x00000550 +#define DSI_PHY_CAL_STATUS_CAL_BUSY 0x00000010 + + +#endif /* DSI_XML */
drivers/gpu/drm/msm/dsi/mmss_cc.xml.h new file +156 -0
156 lines not on this page · raw message
drivers/gpu/drm/msm/dsi/sfpb.xml.h new file +46 -0
46 lines not on this page · raw message
drivers/gpu/drm/msm/hdmi/hdmi.xml.h new file +508 -0
508 lines not on this page · raw message
drivers/gpu/drm/msm/hdmi/qfprom.xml.h new file +50 -0
50 lines not on this page · raw message
drivers/gpu/drm/msm/mdp4/mdp4.xml.h new file +1001 -0
1,001 lines not on this page · raw message
Signature · 1 line
1.8.1.4
RFC 2/2 drm/msm: basic KMS driver for snapdragon
In reply to Rob ClarkThe snapdragon chips have multiple different display controllers, depending on which chip variant/version. (As far as I can tell, current devices have either MDP3 or MDP4, and upcoming devices have MDSS.) And then external to the display controller are HDMI, DSI, etc. blocks which may be shared across devices which have different display controller blocks.
To more easily add support for different display controller blocks, the display controller specific bits are split out into a "kms" object, which provides the kms plane/crtc/encoder objects.
The external HDMI, DSI, etc. blocks are part encoder, and part connector currently. But I think I will pull in the drm_bridge patches from chromeos tree, and split them into a bridge+connector, with the registers that need to be set in modeset handled by the bridge. This would remove the 'msm_connector' base class. But some things need to be double checked to make sure I could get the correct ON/OFF sequencing..
--- drivers/gpu/drm/Kconfig | 2 + drivers/gpu/drm/Makefile | 1 + drivers/gpu/drm/msm/Kconfig | 34 ++ drivers/gpu/drm/msm/Makefile | 23 ++ drivers/gpu/drm/msm/NOTES | 43 +++ drivers/gpu/drm/msm/hdmi/hdmi_connector.c | 528 ++++++++++++++++++++++++++++ drivers/gpu/drm/msm/hdmi/hdmi_connector.h | 95 +++++ drivers/gpu/drm/msm/hdmi/hdmi_i2c.c | 264 ++++++++++++++ drivers/gpu/drm/msm/hdmi/hdmi_phy_8960.c | 140 ++++++++ drivers/gpu/drm/msm/hdmi/hdmi_phy_8x60.c | 215 +++++++++++ drivers/gpu/drm/msm/mdp4/mdp4_crtc.c | 440 +++++++++++++++++++++++ drivers/gpu/drm/msm/mdp4/mdp4_dtv_encoder.c | 306 ++++++++++++++++ drivers/gpu/drm/msm/mdp4/mdp4_irq.c | 194 ++++++++++ drivers/gpu/drm/msm/mdp4/mdp4_kms.c | 359 +++++++++++++++++++ drivers/gpu/drm/msm/mdp4/mdp4_kms.h | 161 +++++++++ drivers/gpu/drm/msm/mdp4/mdp4_plane.c | 241 +++++++++++++ drivers/gpu/drm/msm/msm_connector.c | 34 ++ drivers/gpu/drm/msm/msm_connector.h | 68 ++++ drivers/gpu/drm/msm/msm_drv.c | 491 ++++++++++++++++++++++++++ drivers/gpu/drm/msm/msm_drv.h | 161 +++++++++ drivers/gpu/drm/msm/msm_fb.c | 216 ++++++++++++ drivers/gpu/drm/msm/msm_fbdev.c | 255 ++++++++++++++ drivers/gpu/drm/msm/msm_gem.c | 441 +++++++++++++++++++++++ 23 files changed, 4712 insertions(+) create mode 100644 drivers/gpu/drm/msm/Kconfig create mode 100644 drivers/gpu/drm/msm/Makefile create mode 100644 drivers/gpu/drm/msm/NOTES create mode 100644 drivers/gpu/drm/msm/hdmi/hdmi_connector.c create mode 100644 drivers/gpu/drm/msm/hdmi/hdmi_connector.h create mode 100644 drivers/gpu/drm/msm/hdmi/hdmi_i2c.c create mode 100644 drivers/gpu/drm/msm/hdmi/hdmi_phy_8960.c create mode 100644 drivers/gpu/drm/msm/hdmi/hdmi_phy_8x60.c create mode 100644 drivers/gpu/drm/msm/mdp4/mdp4_crtc.c create mode 100644 drivers/gpu/drm/msm/mdp4/mdp4_dtv_encoder.c create mode 100644 drivers/gpu/drm/msm/mdp4/mdp4_irq.c create mode 100644 drivers/gpu/drm/msm/mdp4/mdp4_kms.c create mode 100644 drivers/gpu/drm/msm/mdp4/mdp4_kms.h create mode 100644 drivers/gpu/drm/msm/mdp4/mdp4_plane.c create mode 100644 drivers/gpu/drm/msm/msm_connector.c create mode 100644 drivers/gpu/drm/msm/msm_connector.h create mode 100644 drivers/gpu/drm/msm/msm_drv.c create mode 100644 drivers/gpu/drm/msm/msm_drv.h create mode 100644 drivers/gpu/drm/msm/msm_fb.c create mode 100644 drivers/gpu/drm/msm/msm_fbdev.c create mode 100644 drivers/gpu/drm/msm/msm_gem.c
drivers/gpu/drm/Kconfig +2 -0
@@ -220,3 +220,5 @@ source "drivers/gpu/drm/omapdrm/Kconfig" source "drivers/gpu/drm/tilcdc/Kconfig" source "drivers/gpu/drm/qxl/Kconfig" + +source "drivers/gpu/drm/msm/Kconfig"
drivers/gpu/drm/Makefile +1 -0
@@ -52,4 +52,5 @@ obj-$(CONFIG_DRM_SHMOBILE) +=shmobile/ obj-$(CONFIG_DRM_OMAP) += omapdrm/ obj-$(CONFIG_DRM_TILCDC) += tilcdc/ obj-$(CONFIG_DRM_QXL) += qxl/ +obj-$(CONFIG_DRM_MSM) += msm/ obj-y += i2c/
drivers/gpu/drm/msm/Kconfig new file +34 -0
@@ -0,0 +1,34 @@ + +config DRM_MSM + tristate "MSM DRM" + depends on DRM + depends on ARCH_MSM + depends on ARCH_MSM8960 + select DRM_KMS_HELPER + select SHMEM + select TMPFS + default y + help + DRM/KMS driver for MSM/snapdragon. + +config DRM_MSM_FBDEV + bool "Enable legacy fbdev support for MSM modesetting driver" + depends on DRM_MSM + select FB_SYS_FILLRECT + select FB_SYS_COPYAREA + select FB_SYS_IMAGEBLIT + select FB_SYS_FOPS + default y + help + Choose this option if you have a need for the legacy fbdev + support. Note that this support also provide the linux console + support on top of the MSM modesetting driver. + +config DRM_MSM_REGISTER_LOGGING + bool "MSM DRM register logging" + depends on DRM_MSM + default n + help + Compile in support for logging register reads/writes in a format + that can be parsed by envytools demsm tool. If enabled, register + logging can be switched on via msm.reglog=y module param.
drivers/gpu/drm/msm/Makefile new file +23 -0
@@ -0,0 +1,23 @@ +ccflags-y := -Iinclude/drm +ifeq (, $(findstring -W,$(EXTRA_CFLAGS))) + ccflags-y += -Werror +endif + +msm-y := \ + hdmi/hdmi_connector.o \ + hdmi/hdmi_i2c.o \ + hdmi/hdmi_phy_8960.o \ + hdmi/hdmi_phy_8x60.o \ + mdp4/mdp4_crtc.o \ + mdp4/mdp4_dtv_encoder.o \ + mdp4/mdp4_irq.o \ + mdp4/mdp4_kms.o \ + mdp4/mdp4_plane.o \ + msm_connector.o \ + msm_drv.o \ + msm_fb.o \ + msm_gem.o + +msm-$(CONFIG_DRM_MSM_FBDEV) += msm_fbdev.o + +obj-$(CONFIG_DRM_MSM) += msm.o
drivers/gpu/drm/msm/NOTES new file +43 -0
43 lines not on this page · raw message
drivers/gpu/drm/msm/hdmi/hdmi_connector.c new file +528 -0
528 lines not on this page · raw message
drivers/gpu/drm/msm/hdmi/hdmi_connector.h new file +95 -0
95 lines not on this page · raw message
drivers/gpu/drm/msm/hdmi/hdmi_i2c.c new file +264 -0
264 lines not on this page · raw message
drivers/gpu/drm/msm/hdmi/hdmi_phy_8960.c new file +140 -0
140 lines not on this page · raw message
drivers/gpu/drm/msm/hdmi/hdmi_phy_8x60.c new file +215 -0
215 lines not on this page · raw message
drivers/gpu/drm/msm/mdp4/mdp4_crtc.c new file +440 -0
440 lines not on this page · raw message
drivers/gpu/drm/msm/mdp4/mdp4_dtv_encoder.c new file +306 -0
306 lines not on this page · raw message
drivers/gpu/drm/msm/mdp4/mdp4_irq.c new file +194 -0
194 lines not on this page · raw message
drivers/gpu/drm/msm/mdp4/mdp4_kms.c new file +359 -0
359 lines not on this page · raw message
drivers/gpu/drm/msm/mdp4/mdp4_kms.h new file +161 -0
161 lines not on this page · raw message
drivers/gpu/drm/msm/mdp4/mdp4_plane.c new file +241 -0
188 lines not on this page · raw message
drivers/gpu/drm/msm/msm_connector.c new file +34 -0
Lines of this file are not shown. View the raw message.
drivers/gpu/drm/msm/msm_connector.h new file +68 -0
68 lines not on this page · raw message
drivers/gpu/drm/msm/msm_drv.c new file +491 -0
491 lines not on this page · raw message
drivers/gpu/drm/msm/msm_drv.h new file +161 -0
161 lines not on this page · raw message
drivers/gpu/drm/msm/msm_fb.c new file +216 -0
216 lines not on this page · raw message
drivers/gpu/drm/msm/msm_fbdev.c new file +255 -0
255 lines not on this page · raw message
drivers/gpu/drm/msm/msm_gem.c new file +441 -0
441 lines not on this page · raw message
The rest of this diff is not shown. View the raw message.
Signature · 1 line
1.8.1.4
Part of the diff is not shown here. View the raw message.
Re: [RFCv1 2/2] drm/msm: basic KMS driver for snapdragon
Quoted 34 lines · Rob Clark
The snapdragon chips have multiple different display controllers, depending on which chip variant/version. (As far as I can tell, current devices have either MDP3 or MDP4, and upcoming devices have MDSS.) And then external to the display controller are HDMI, DSI, etc. blocks which may be shared across devices which have different display controller blocks.
To more easily add support for different display controller blocks, the display controller specific bits are split out into a "kms" object, which provides the kms plane/crtc/encoder objects.
The external HDMI, DSI, etc. blocks are part encoder, and part connector currently. But I think I will pull in the
drm_bridgepatches from chromeos tree, and split them into a bridge+connector, with the registers that need to be set in modeset handled by the bridge. This would remove the 'msm_connector' base class. But some things need to be double checked to make sure I could get the correct ON/OFF sequencing..Signed-off-by Rob Clark <[email protected]>drivers/gpu/drm/msm/NOTES new file +8
-0@@ -0,0 +1,43 @@ +Rough thoughts/notes.. + +We have (at least) 3 different display controller blocks at play: + + MDP3 - ?? seems to be what is on geeksphone peak device + + MDP4 - S3 (APQ8060, touchpad), S4-pro (APQ8064, nexus4 & ifc6410) + + MDSS - snapdragon 800 + +(I don't have a completely clear picture on which display controller
drivers/gpu/drm/msm/NOTES line 18
Quoted 10 lines
drivers/gpu/drm/msm/NOTES new file +10
-0+is in which devices) + +But, HDMI/DSI/etc blocks seem like they can be shared. And I for sure +don't want to have to deal with N different kms devices from +xf86-video-freedreno. Plus, it seems like we can do some clever tricks +like have kms/crtc code build up gpu cmdstream to update scanout after +rendering without involving the cpu. + +And on gpu side of things: + + zero, one, or two 2d cores (z180)
Life would be easier if we just forgot that z180 existed.
drivers/gpu/drm/msm/NOTES line 19
Quoted 1 line
drivers/gpu/drm/msm/NOTES new file +1
-0+ + and either a2xx or a3xx 3d core.
A2XX will probably be less interesting to everybody except folks trying to get their ancient phones working. That said it might be smart to keep the GPU sub device split because future.
drivers/gpu/drm/msm/NOTES line 23
Quoted 4 lines
drivers/gpu/drm/msm/NOTES new file +4
-0+ +So, one drm driver, with some modularity. Different 'struct msm_kms' +implementations, depending on display controller. And one or more +'struct msm_gpu' for the various different gpu sub-modules.
If Z180 goes poof then we could conceivably use 'adreno' for a name which is a nice way to compartmentalize the GPU code. On the other hand msm_gpu has consistency going for it.
Quoted 67 lines
drivers/gpu/drm/msm/NOTES new file +19
-0+The kms module provides the plane, crtc, and encoder objects, and +loads whatever connectors are appropriate. + +For MDP4, the mapping is (I think): + + plane -> PIPE{RGBn,VGn} \ + crtc -> OVLP{n} + DMA{P,S,E} (??) |-> MDP "device" + encoder -> DTV/LCDC/DSI (within MDP4) / + connector -> HDMI/DSI/etc --> other device(s) + +Since the irq's that drm core mostly cares about are vblank/framedone, +we'll let msm_mdp4_kms provide the irq install/uninstall/etc functions +and treat the MDP4 block's irq as "the" irq. Even though the connectors +may have their own irqs which they install themselves. For this reason +the display controller is the "master" device. + +Each connector probably ends up being a seperate device, just for the +logistics of finding/mapping io region, irq, etc. +drivers/gpu/drm/msm/msm_drv.c new file +41
-0@@ -0,0 +1,491 @@ +/* +* Copyright (C) 2013 Red Hat +* Author: Rob Clark <[email protected]> +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License version 2 as published by +* the Free Software Foundation. +* +* This program is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +* more details. +* +* You should have received a copy of the GNU General Public License along with +* this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "msm_drv.h" + +static void msm_fb_output_poll_changed (struct drm_device *dev) +{ +struct msm_drm_private *priv = dev->dev_private; +if (priv->fbdev) +drm_fb_helper_hotplug_event (priv->fbdev); +} + +static const struct drm_mode_config_funcs mode_config_funcs = { + .fb_create = msm_framebuffer_create, + .output_poll_changed = msm_fb_output_poll_changed, +}; + +static int msm_fault_handler (struct iommu_domain *iommu,struct device *dev, +unsigned long iova,int flags) +{ +DBG ("*** fault: iova=%08lx, flags=%d" , iova, flags); +return 0 ; +} + +int msm_register_iommu (struct drm_device *dev,struct iommu_domain *iommu) +{ +struct msm_drm_private *priv = dev->dev_private;
drivers/gpu/drm/msm/msm_drv.c line 51
Quoted 10 lines
drivers/gpu/drm/msm/msm_drv.c new file +10
-0+int idx = priv->num_iommus++; + +if (WARN_ON (idx >=ARRAY_SIZE (priv->iommus))) +return -EINVAL ; + + priv->iommus[idx] = iommu; + +iommu_set_fault_handler (iommu, msm_fault_handler); + +/* need to iommu_attach_device() somewhere?? on resume?? */
We are going to end up with 2 IOMMUs to deal with.
Quoted 164 lines
drivers/gpu/drm/msm/msm_drv.c new file +164
-0+return idx; +} + +#ifdef CONFIG_DRM_MSM_REGISTER_LOGGING +static bool reglog = false; +MODULE_PARM_DESC (reglog,"Enable register read/write logging" ); +module_param (reglog, bool,0600 ); +#else +#define reglog 0 +#endif + +void __iomem *msm_ioremap (struct device *dev,resource_size_t offset, +unsigned long size,const char *name) +{ +void __iomem *ptr =devm_ioremap_nocache (dev, offset, size); +if (reglog) +printk (KERN_DEBUG "IO:region %s %08x %08lx\n" , name, (u32 )ptr, size); +return ptr; +} + +void msm_writel (u32 data,void __iomem *addr) +{ +if (reglog) +printk (KERN_DEBUG "IO:W %08x %08x\n" , (u32 )addr, data); +writel (data, addr); +} + +u32 msm_readl (const void __iomem *addr) +{ +u32 val =readl (addr); +if (reglog) +printk (KERN_ERR "IO:R %08x %08x\n" , (u32 )addr, val); +return val; +} + +/* +* DRM operations: +*/ + +static int msm_unload (struct drm_device *dev) +{ +struct msm_drm_private *priv = dev->dev_private; +struct msm_kms *kms = priv->kms; + +drm_kms_helper_poll_fini (dev); +drm_mode_config_cleanup (dev); +drm_vblank_cleanup (dev); + +pm_runtime_get_sync (dev->dev); +drm_irq_uninstall (dev); +pm_runtime_put_sync (dev->dev); + +flush_workqueue (priv->wq); +destroy_workqueue (priv->wq); + +if (kms) { +pm_runtime_disable (dev->dev); + kms->funcs->destroy (kms); + } + + dev->dev_private =NULL ; + +pm_runtime_disable (dev->dev); + +kfree (priv); + +return 0 ; +} + +static int msm_load (struct drm_device *dev,unsigned long flags) +{ +struct platform_device *pdev = dev->platformdev; +struct msm_drm_private *priv; +struct msm_kms *kms; +int ret; + + priv =kzalloc (sizeof (*priv),GFP_KERNEL ); +if (!priv) { +dev_err (dev->dev,"failed to allocate private data\n" ); +return -ENOMEM ; + } + + dev->dev_private = priv; + + priv->wq =alloc_ordered_workqueue ("msm" ,0 ); + +INIT_LIST_HEAD (&priv->obj_list); + +drm_mode_config_init (dev); + + kms =mdp4_kms_init (dev); +if (IS_ERR (kms)) { +/* +* NOTE: once we have GPU support, having no kms should not +* be considered fatal.. ideally we would still support gpu +* and (for example) use dmabuf/prime to share buffers with +* imx drm driver on iMX5 +*/ +dev_err (dev->dev,"failed to load kms\n" ); + ret =PTR_ERR (priv->kms); + goto fail; + } + + priv->kms = kms; + +if (kms) { +pm_runtime_enable (dev->dev); + ret = kms->funcs->hw_init (kms); +if (ret) { +dev_err (dev->dev,"kms hw init failed: %d\n" , ret); + goto fail; + } + } + + dev->mode_config.min_width =0 ; + dev->mode_config.min_height =0 ; + dev->mode_config.max_width =2048 ; + dev->mode_config.max_height =2048 ; + dev->mode_config.funcs = &mode_config_funcs; + + ret =drm_vblank_init (dev,1 ); +if (ret <0 ) { +dev_err (dev->dev,"failed to initialize vblank\n" ); + goto fail; + } + +pm_runtime_get_sync (dev->dev); + ret =drm_irq_install (dev); +pm_runtime_put_sync (dev->dev); +if (ret <0 ) { +dev_err (dev->dev,"failed to install IRQ handler\n" ); + goto fail; + } + +platform_set_drvdata (pdev, dev); + +#ifdef CONFIG_DRM_MSM_FBDEV + priv->fbdev =msm_fbdev_init (dev); +#endif + +drm_kms_helper_poll_init (dev); + +return 0 ; + +fail: +msm_unload (dev); +return ret; +} + +static void msm_preclose (struct drm_device *dev,struct drm_file *file) +{ +struct msm_drm_private *priv = dev->dev_private; +struct msm_kms *kms = priv->kms; +if (kms) + kms->funcs->preclose (kms, file); +} + +static void msm_lastclose (struct drm_device *dev) +{ +struct msm_drm_private *priv = dev->dev_private; +if (priv->fbdev) { +drm_modeset_lock_all (dev); +drm_fb_helper_restore_fbdev_mode (priv->fbdev); +drm_modeset_unlock_all (dev);
drivers/gpu/drm/msm/msm_drv.c line 226
Quoted 10 lines
drivers/gpu/drm/msm/msm_drv.c new file +10
-0+ } +} + +static irqreturn_t msm_irq (DRM_IRQ_ARGS ) +{ +struct drm_device *dev = arg; +struct msm_drm_private *priv = dev->dev_private; +struct msm_kms *kms = priv->kms; +BUG_ON (!kms); +return kms->funcs->irq (kms);
And we will have separate interrupts too - has anybody else had to deal with that (too lazy to check).
Quoted 704 lines
drivers/gpu/drm/msm/msm_drv.c new file +265
-0+} + +static void msm_irq_preinstall (struct drm_device *dev) +{ +struct msm_drm_private *priv = dev->dev_private; +struct msm_kms *kms = priv->kms; +BUG_ON (!kms); + kms->funcs->irq_preinstall (kms); +} + +static int msm_irq_postinstall (struct drm_device *dev) +{ +struct msm_drm_private *priv = dev->dev_private; +struct msm_kms *kms = priv->kms; +BUG_ON (!kms); +return kms->funcs->irq_postinstall (kms); +} + +static void msm_irq_uninstall (struct drm_device *dev) +{ +struct msm_drm_private *priv = dev->dev_private; +struct msm_kms *kms = priv->kms; +BUG_ON (!kms); + kms->funcs->irq_uninstall (kms); +} + +static int msm_enable_vblank (struct drm_device *dev,int crtc_id) +{ +struct msm_drm_private *priv = dev->dev_private; +struct msm_kms *kms = priv->kms; +if (!kms) +return -ENXIO ; +DBG ("dev=%p, crtc=%d" , dev, crtc_id); +return kms->funcs->enable_vblank (kms, priv->crtcs[crtc_id]); +} + +static void msm_disable_vblank (struct drm_device *dev,int crtc_id) +{ +struct msm_drm_private *priv = dev->dev_private; +struct msm_kms *kms = priv->kms; +if (!kms) +return ; +DBG ("dev=%p, crtc=%d" , dev, crtc_id); + kms->funcs->disable_vblank (kms, priv->crtcs[crtc_id]); +} + +#ifdef CONFIG_DEBUG_FS +static int msm_gem_show (struct seq_file *m,void *arg) +{ +struct drm_info_node *node = (struct drm_info_node *) m->private; +struct drm_device *dev = node->minor->dev; +struct msm_drm_private *priv = dev->dev_private; +int ret; + + ret =mutex_lock_interruptible (&dev->struct_mutex); +if (ret) +return ret; + +seq_printf (m,"All Objects:\n" ); +msm_gem_describe_objects (&priv->obj_list, m); + +mutex_unlock (&dev->struct_mutex); + +return 0 ; +} + +static int msm_mm_show (struct seq_file *m,void *arg) +{ +struct drm_info_node *node = (struct drm_info_node *) m->private; +struct drm_device *dev = node->minor->dev; +return drm_mm_dump_table (m, dev->mm_private); +} + +static int msm_fb_show (struct seq_file *m,void *arg) +{ +struct drm_info_node *node = (struct drm_info_node *) m->private; +struct drm_device *dev = node->minor->dev; +struct msm_drm_private *priv = dev->dev_private; +struct drm_framebuffer *fb, *fbdev_fb =NULL ; + +if (priv->fbdev) { +seq_printf (m,"fbcon " ); + fbdev_fb = priv->fbdev->fb; +msm_framebuffer_describe (fbdev_fb, m); + } + +mutex_lock (&dev->mode_config.fb_lock); +list_for_each_entry (fb, &dev->mode_config.fb_list, head) { +if (fb == fbdev_fb) +continue ; + +seq_printf (m,"user " ); +msm_framebuffer_describe (fb, m); + } +mutex_unlock (&dev->mode_config.fb_lock); + +return 0 ; +} + +static struct drm_info_list msm_debugfs_list[] = { + {"gem" , msm_gem_show,0 }, + {"mm" , msm_mm_show,0 }, + {"fb" , msm_fb_show,0 }, +}; + +static int msm_debugfs_init (struct drm_minor *minor) +{ +struct drm_device *dev = minor->dev; +int ret; + + ret =drm_debugfs_create_files (msm_debugfs_list, +ARRAY_SIZE (msm_debugfs_list), + minor->debugfs_root, minor); + +if (ret) { +dev_err (dev->dev,"could not install msm_debugfs_list\n" ); +return ret; + } + +return ret; +} + +static void msm_debugfs_cleanup (struct drm_minor *minor) +{ +drm_debugfs_remove_files (msm_debugfs_list, +ARRAY_SIZE (msm_debugfs_list), minor); +} +#endif + +static const struct vm_operations_struct vm_ops = { + .fault = msm_gem_fault, + .open = drm_gem_vm_open, + .close = drm_gem_vm_close, +}; + +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, + .fasync = drm_fasync, + .llseek = no_llseek, + .mmap = msm_gem_mmap, +}; + +static struct drm_driver msm_driver = { + .driver_features =DRIVER_HAVE_IRQ |DRIVER_GEM |DRIVER_MODESET , + .load = msm_load, + .unload = msm_unload, + .preclose = msm_preclose, + .lastclose = msm_lastclose, + .irq_handler = msm_irq, + .irq_preinstall = msm_irq_preinstall, + .irq_postinstall = msm_irq_postinstall, + .irq_uninstall = msm_irq_uninstall, + .get_vblank_counter = drm_vblank_count, + .enable_vblank = msm_enable_vblank, + .disable_vblank = msm_disable_vblank, + .gem_free_object = msm_gem_free_object, + .gem_vm_ops = &vm_ops, + .dumb_create = msm_gem_dumb_create, + .dumb_map_offset = msm_gem_dumb_map_offset, + .dumb_destroy = msm_gem_dumb_destroy, +#ifdef CONFIG_DEBUG_FS + .debugfs_init = msm_debugfs_init, + .debugfs_cleanup = msm_debugfs_cleanup, +#endif + .fops = &fops, + .name ="msm" , + .desc ="MSM Snapdragon DRM" , + .date ="20130625" , + .major =1 , + .minor =0 , +}; + +#ifdef CONFIG_PM_SLEEP +static int msm_pm_suspend (struct device *dev) +{ +struct drm_device *ddev =dev_get_drvdata (dev); +struct msm_drm_private *priv = ddev->dev_private; +struct msm_kms *kms = priv->kms; + +drm_kms_helper_poll_disable (ddev); + +return kms->funcs->pm_suspend (kms); +} + +static int msm_pm_resume (struct device *dev) +{ +struct drm_device *ddev =dev_get_drvdata (dev); +struct msm_drm_private *priv = ddev->dev_private; +struct msm_kms *kms = priv->kms; +int ret =0 ; + + ret = kms->funcs->pm_resume (kms); +if (ret) +return ret; + +drm_kms_helper_poll_enable (ddev); + +return 0 ; +} +#endif + +static const struct dev_pm_ops msm_pm_ops = { +SET_SYSTEM_SLEEP_PM_OPS (msm_pm_suspend, msm_pm_resume) +}; + +/* +* Platform driver: +*/ + +static int msm_pdev_probe (struct platform_device *pdev) +{ +return drm_platform_init (&msm_driver, pdev); +} + +static int msm_pdev_remove (struct platform_device *pdev) +{ +drm_platform_exit (&msm_driver, pdev); + +return 0 ; +} + +static const struct platform_device_id msm_id[] = { + {"mdp" ,0 }, + { } +}; + +static struct platform_driver msm_platform_driver = { + .probe = msm_pdev_probe, + .remove = msm_pdev_remove, + .driver = { + .owner =THIS_MODULE , + .name ="msm" , + .pm = &msm_pm_ops, + }, + .id_table = msm_id, +}; + +static int __init msm_drm_init (void ) +{ +DBG ("init" ); +hdmi_init (); +return platform_driver_register (&msm_platform_driver); +} + +static void __exit msm_drm_fini(void) +{ +DBG ("fini" ); +platform_driver_unregister (&msm_platform_driver); +hdmi_fini (); +} + +module_init (msm_drm_init); +module_exit (msm_drm_fini); + +MODULE_AUTHOR ("Rob Clark <[email protected]" ); +MODULE_DESCRIPTION ("MSM DRM Driver" ); +MODULE_LICENSE ("GPL" );drivers/gpu/drm/msm/msm_gem.c new file +431
-0@@ -0,0 +1,441 @@ +/* +* Copyright (C) 2013 Red Hat +* Author: Rob Clark <[email protected]> +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License version 2 as published by +* the Free Software Foundation. +* +* This program is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +* more details. +* +* You should have received a copy of the GNU General Public License along with +* this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <linux/spinlock.h> +#include <linux/shmem_fs.h> + +#include "msm_drv.h" + +struct msm_gem_object { +struct drm_gem_object base; + +struct list_head mm_list; + +uint32_t flags; +struct page **pages; +struct sg_table *sgt; +void *vaddr; + +struct { +// XXX +uint32_t iova; + } domain[NUM_DOMAINS ]; +}; +#define to_msm_bo (x) container_of(x, struct msm_gem_object, base) + +/* called with dev->struct_mutex held */ +/* TODO move this into drm_gem.c */ +static struct page **attach_pages (struct drm_gem_object *obj) +{ +struct inode *inode; +struct address_space *mapping; +struct page *p, **pages; +int i, npages; + +/* This is the shared memory object that backs the GEM resource */ + inode =file_inode (obj->filp); + mapping = inode->i_mapping; + + npages = obj->size >>PAGE_SHIFT ; + + pages =drm_malloc_ab (npages,sizeof (struct page *)); +if (pages ==NULL ) +return ERR_PTR (-ENOMEM ); + +for (i =0 ; i < npages; i++) { + p =shmem_read_mapping_page (mapping, i); +if (IS_ERR (p)) + goto fail; + pages[i] = p; + } + +return pages; + +fail: +while (i--) +page_cache_release (pages[i]); + +drm_free_large (pages); +return ERR_CAST (p); +} + +static void detach_pages (struct drm_gem_object *obj,struct page **pages) +{ +int i, npages; + + npages = obj->size >>PAGE_SHIFT ; + +for (i =0 ; i < npages; i++) { +set_page_dirty (pages[i]); + +/* Undo the reference we took when populating the table */ +page_cache_release (pages[i]); + } + +drm_free_large (pages); +} + + +/* called with dev->struct_mutex held */ +static struct page **get_pages (struct drm_gem_object *obj) +{ +struct msm_gem_object *msm_obj =to_msm_bo (obj); + +if (!msm_obj->pages) { +struct page **p =attach_pages (obj); +int npages = obj->size >>PAGE_SHIFT ; + +if (IS_ERR (p)) { +dev_err (obj->dev->dev,"could not get pages: %ld\n" , +PTR_ERR (p)); +return p; + } + msm_obj->pages = p; + msm_obj->sgt =drm_prime_pages_to_sg (p, npages); + } + +return msm_obj->pages; +} + +static void put_pages (struct drm_gem_object *obj) +{ +struct msm_gem_object *msm_obj =to_msm_bo (obj); + +if (!msm_obj->pages) { +if (msm_obj->sgt) { +sg_free_table (msm_obj->sgt); +kfree (msm_obj->sgt); + } +detach_pages (obj, msm_obj->pages); + msm_obj->pages =NULL ; + } +} + +int msm_gem_mmap_obj (struct drm_gem_object *obj, +struct vm_area_struct *vma) +{ +struct msm_gem_object *msm_obj =to_msm_bo (obj); + + vma->vm_flags &= ~VM_PFNMAP ; + vma->vm_flags |=VM_MIXEDMAP ; + +if (msm_obj->flags &MSM_BO_WC ) { + vma->vm_page_prot =pgprot_writecombine (vm_get_page_prot (vma->vm_flags)); + }else if (msm_obj->flags &MSM_BO_UNCACHED ) { + vma->vm_page_prot =pgprot_noncached (vm_get_page_prot (vma->vm_flags)); + }else { +/* +* Shunt off cached objs to shmem file so they have their own +* address_space (so unmap_mapping_range does what we want, +* in particular in the case of mmap'd dmabufs) +*/ +fput (vma->vm_file); +get_file (obj->filp); + vma->vm_pgoff =0 ; + vma->vm_file = obj->filp; + + vma->vm_page_prot =vm_get_page_prot (vma->vm_flags); + } + +return 0 ; +} + +int msm_gem_mmap (struct file *filp,struct vm_area_struct *vma) +{ +int ret; + + ret =drm_gem_mmap (filp, vma); +if (ret) { +DBG ("mmap failed: %d" , ret); +return ret; + } + +return msm_gem_mmap_obj (vma->vm_private_data, vma); +} + +int msm_gem_fault (struct vm_area_struct *vma,struct vm_fault *vmf) +{ +struct drm_gem_object *obj = vma->vm_private_data; +struct msm_gem_object *msm_obj =to_msm_bo (obj); +struct drm_device *dev = obj->dev; +struct page **pages; +unsigned long pfn; +pgoff_t pgoff; +int ret; + +/* Make sure we don't parallel update on a fault, nor move or remove +* something from beneath our feet +*/ +mutex_lock (&dev->struct_mutex); + +/* make sure we have pages attached now */ + pages =get_pages (obj); +if (IS_ERR (pages)) { + ret =PTR_ERR (pages); + goto out; + } + +/* We don't use vmf->pgoff since that has the fake offset: */ + pgoff = ((unsigned long )vmf->virtual_address - + vma->vm_start) >>PAGE_SHIFT ; + + pfn =page_to_pfn (msm_obj->pages[pgoff]); + +VERB ("Inserting %p pfn %lx, pa %lx" , vmf->virtual_address, + pfn, pfn <<PAGE_SHIFT ); + + ret =vm_insert_mixed (vma, (unsigned long )vmf->virtual_address, pfn); + +out: +mutex_unlock (&dev->struct_mutex); +switch (ret) { +case 0 : +case -ERESTARTSYS : +case -EINTR : +return VM_FAULT_NOPAGE ; +case -ENOMEM : +return VM_FAULT_OOM ; +default : +return VM_FAULT_SIGBUS ; + } +} + +/** get mmap offset */ +static uint64_t mmap_offset (struct drm_gem_object *obj) +{ +struct drm_device *dev = obj->dev; + +WARN_ON (!mutex_is_locked (&dev->struct_mutex)); + +if (!obj->map_list.map) { +/* Make it mmapable */ +int ret =drm_gem_create_mmap_offset (obj); + +if (ret) { +dev_err (dev->dev,"could not allocate mmap offset\n" ); +return 0 ; + } + } + +return (uint64_t )obj->map_list.hash.key <<PAGE_SHIFT ; +} + +uint64_t msm_gem_mmap_offset (struct drm_gem_object *obj) +{ +uint64_t offset; +mutex_lock (&obj->dev->struct_mutex); + offset =mmap_offset (obj); +mutex_unlock (&obj->dev->struct_mutex); +return offset; +} + +int msm_gem_get_iova (struct drm_gem_object *obj,int id,uint32_t *iova) +{ +struct msm_gem_object *msm_obj =to_msm_bo (obj); +int ret =0 ; + +mutex_lock (&obj->dev->struct_mutex); +if (!msm_obj->domain[id].iova) { +struct msm_drm_private *priv = obj->dev->dev_private; +uint32_t offset = (uint32_t )mmap_offset (obj); +get_pages (obj); + ret =iommu_map_range (priv->iommus[id], offset, + msm_obj->sgt->sgl, obj->size,IOMMU_READ ); + msm_obj->domain[id].iova = offset; + } +mutex_unlock (&obj->dev->struct_mutex); + +if (!ret) + *iova = msm_obj->domain[id].iova; + +return ret; +} + +void msm_gem_put_iova (struct drm_gem_object *obj,int id) +{ +} + +int msm_gem_dumb_create (struct drm_file *file,struct drm_device *dev, +struct drm_mode_create_dumb *args) +{ + args->pitch =align_pitch (args->width, args->bpp); + args->size =PAGE_ALIGN (args->pitch * args->height); +return msm_gem_new_handle (dev, file, args->size, +MSM_BO_SCANOUT |MSM_BO_WC , &args->handle); +} + +int msm_gem_dumb_destroy (struct drm_file *file,struct drm_device *dev, +uint32_t handle) +{ +/* No special work needed, drop the reference and see what falls out */ +return drm_gem_handle_delete (file, handle); +} + +int msm_gem_dumb_map_offset (struct drm_file *file,struct drm_device *dev, +uint32_t handle,uint64_t *offset) +{ +struct drm_gem_object *obj; +int ret =0 ; + +/* GEM does all our handle to object mapping */ + obj =drm_gem_object_lookup (dev, file, handle); +if (obj ==NULL ) { + ret = -ENOENT ; + goto fail; + } + + *offset =msm_gem_mmap_offset (obj); + +drm_gem_object_unreference_unlocked (obj); + +fail: +return ret; +} + +void *msm_gem_vaddr (struct drm_gem_object *obj) +{ +struct msm_gem_object *msm_obj =to_msm_bo (obj); +WARN_ON (!mutex_is_locked (&obj->dev->struct_mutex)); +if (!msm_obj->vaddr) { +struct page **pages =get_pages (obj); +if (IS_ERR (pages)) +return ERR_CAST (pages); + msm_obj->vaddr =vmap (pages, obj->size >>PAGE_SHIFT , +VM_MAP ,pgprot_writecombine (PAGE_KERNEL )); + } +return msm_obj->vaddr; +} + +#ifdef CONFIG_DEBUG_FS +void msm_gem_describe (struct drm_gem_object *obj,struct seq_file *m) +{ +struct drm_device *dev = obj->dev; +struct msm_gem_object *msm_obj =to_msm_bo (obj); +uint64_t off =0 ; + +WARN_ON (!mutex_is_locked (&dev->struct_mutex)); + +if (obj->map_list.map) + off = (uint64_t )obj->map_list.hash.key; + +seq_printf (m,"%08x: %2d (%2d) %08llx %p %d\n" , + msm_obj->flags, obj->name, obj->refcount.refcount.counter, + off, msm_obj->vaddr, obj->size); +} + +void msm_gem_describe_objects (struct list_head *list,struct seq_file *m) +{ +struct msm_gem_object *msm_obj; +int count =0 ; +size_t size =0 ; + +list_for_each_entry (msm_obj, list, mm_list) { +struct drm_gem_object *obj = &msm_obj->base; +seq_printf (m," " ); +msm_gem_describe (obj, m); + count++; + size += obj->size; + } + +seq_printf (m,"Total %d objects, %zu bytes\n" , count, size); +} +#endif + +void msm_gem_free_object (struct drm_gem_object *obj) +{ +struct drm_device *dev = obj->dev; +struct msm_gem_object *msm_obj =to_msm_bo (obj); +int id; + +WARN_ON (!mutex_is_locked (&dev->struct_mutex)); + +list_del (&msm_obj->mm_list); + +if (obj->map_list.map) +drm_gem_free_mmap_offset (obj); + +if (msm_obj->vaddr) +vunmap (msm_obj->vaddr); + +for (id =0 ; id <ARRAY_SIZE (msm_obj->domain); id++) { +if (msm_obj->domain[id].iova) { +struct msm_drm_private *priv = obj->dev->dev_private; +uint32_t offset = (uint32_t )mmap_offset (obj); +iommu_unmap_range (priv->iommus[id], offset, obj->size); + } + } + +put_pages (obj); + +drm_gem_object_release (obj); + +kfree (obj); +} + +/* convenience method to construct a GEM buffer object, and userspace handle */ +int msm_gem_new_handle (struct drm_device *dev,struct drm_file *file, +uint32_t size,uint32_t flags,uint32_t *handle) +{ +struct drm_gem_object *obj; +int ret; + + obj =msm_gem_new (dev, size, flags); +if (!obj) +return -ENOMEM ; + + ret =drm_gem_handle_create (file, obj, handle); + +/* drop reference from allocate - handle holds it now */ +drm_gem_object_unreference_unlocked (obj); + +return ret; +} + +struct drm_gem_object *msm_gem_new (struct drm_device *dev, +uint32_t size,uint32_t flags) +{ +struct msm_drm_private *priv = dev->dev_private; +struct msm_gem_object *msm_obj; +struct drm_gem_object *obj =NULL ; +int ret; + + size =PAGE_ALIGN (size); + + msm_obj =kzalloc (sizeof (*msm_obj),GFP_KERNEL ); +if (!msm_obj) + goto fail; + + obj = &msm_obj->base; + + ret =drm_gem_object_init (dev, obj, size); +if (ret) + goto fail; + + msm_obj->flags = flags; + +mutex_lock (&obj->dev->struct_mutex); +list_add (&msm_obj->mm_list, &priv->obj_list);
drivers/gpu/drm/msm/msm_gem.c line 441
Quoted 10 lines
drivers/gpu/drm/msm/msm_gem.c new file +10
-0+mutex_unlock (&obj->dev->struct_mutex); + +return obj; + +fail: +if (obj) +drm_gem_object_unreference_unlocked (obj); + +return NULL ; +}
Yay GEM. No complaints here.
Jordan
Re: [RFCv1 2/2] drm/msm: basic KMS driver for snapdragon
Quoted 42 lines · Jordan Crouse
Quoted 41 lines · Rob Clark
The snapdragon chips have multiple different display controllers, depending on which chip variant/version. (As far as I can tell, current devices have either MDP3 or MDP4, and upcoming devices have MDSS.) And then external to the display controller are HDMI, DSI, etc. blocks which may be shared across devices which have different display controller blocks.
To more easily add support for different display controller blocks, the display controller specific bits are split out into a "kms" object, which provides the kms plane/crtc/encoder objects.
The external HDMI, DSI, etc. blocks are part encoder, and part connector currently. But I think I will pull in the
drm_bridgepatches from chromeos tree, and split them into a bridge+connector, with the registers that need to be set in modeset handled by the bridge. This would remove the 'msm_connector' base class. But some things need to be double checked to make sure I could get the correct ON/OFF sequencing..Signed-off-by Rob Clark <[email protected]>drivers/gpu/drm/msm/NOTES new file +13
-0@@ -0,0 +1,43 @@ +Rough thoughts/notes.. + +We have (at least) 3 different display controller blocks at play: + + MDP3 - ?? seems to be what is on geeksphone peak device + + MDP4 - S3 (APQ8060, touchpad), S4-pro (APQ8064, nexus4 & ifc6410) + + MDSS - snapdragon 800 + +(I don't have a completely clear picture on which display controller +is in which devices) + +But, HDMI/DSI/etc blocks seem like they can be shared. And I for sure +don't want to have to deal with N different kms devices from +xf86-video-freedreno. Plus, it seems like we can do some clever tricks
Quoted 9 lines
drivers/gpu/drm/msm/NOTES line 18
Quoted 5 lines
drivers/gpu/drm/msm/NOTES new file +5
-0+like have kms/crtc code build up gpu cmdstream to update scanout after +rendering without involving the cpu. + +And on gpu side of things: + + zero, one, or two 2d cores (z180)Life would be easier if we just forgot that z180 existed.
I would like to support it eventually, although not the highest priority. Although I'm not quite sure yet about how to do a sane kernel interface for it.. I might just take the easy way out and memcpy. Regarding extra level of indirection, well it doesn't absolutely *have* to be the same ioctl.. I do need to give it some thought though.
Quoted 8 lines
drivers/gpu/drm/msm/NOTES line 19
Quoted 2 lines
drivers/gpu/drm/msm/NOTES new file +1
-0+ + and either a2xx or a3xx 3d core.A2XX will probably be less interesting to everybody except folks trying to get their ancient phones working. That said it might be smart to keep the GPU sub device split because future.
I would like to support a2xx as well, if for no other reason than that I have a handful of a2xx devices as well. (Although sometimes there is a shortage of # of hrs in a day.)
Quoted 11 lines
drivers/gpu/drm/msm/NOTES line 23
Quoted 5 lines
drivers/gpu/drm/msm/NOTES new file +4
-0+ +So, one drm driver, with some modularity. Different 'struct msm_kms' +implementations, depending on display controller. And one or more +'struct msm_gpu' for the various different gpu sub-modules.If Z180 goes poof then we could conceivably use 'adreno' for a name which is a nice way to compartmentalize the GPU code. On the other hand
msm_gpuhas consistency going for it.
I suppose depending on what marketing literature you read, "adreno" could refer collectively to 2d and 3d cores. But meh. I could go either way on the name.
Quoted 77 lines
Quoted 77 lines
drivers/gpu/drm/msm/NOTES new file +19
-0+The kms module provides the plane, crtc, and encoder objects, and +loads whatever connectors are appropriate. + +For MDP4, the mapping is (I think): + + plane -> PIPE{RGBn,VGn} \ + crtc -> OVLP{n} + DMA{P,S,E} (??) |-> MDP "device" + encoder -> DTV/LCDC/DSI (within MDP4) / + connector -> HDMI/DSI/etc --> other device(s) + +Since the irq's that drm core mostly cares about are vblank/framedone, +we'll let msm_mdp4_kms provide the irq install/uninstall/etc functions +and treat the MDP4 block's irq as "the" irq. Even though the connectors +may have their own irqs which they install themselves. For this reason +the display controller is the "master" device. + +Each connector probably ends up being a seperate device, just for the +logistics of finding/mapping io region, irq, etc. +drivers/gpu/drm/msm/msm_drv.c new file +5
-0@@ -0,0 +1,491 @@ +/* +* Copyright (C) 2013 Red Hat +* Author: Rob Clark <[email protected]> +* +* This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */drivers/gpu/drm/msm/msm_drv.c new file +16
-0+ +#include "msm_drv.h" + +static void msm_fb_output_poll_changed (struct drm_device *dev) +{ +struct msm_drm_private *priv = dev->dev_private; +if (priv->fbdev) +drm_fb_helper_hotplug_event (priv->fbdev); +} + +static const struct drm_mode_config_funcs mode_config_funcs = { + .fb_create = msm_framebuffer_create, + .output_poll_changed = msm_fb_output_poll_changed, +}; + +static int msm_fault_handler(struct iommu_domain *iommu, struct device*dev,
drivers/gpu/drm/msm/msm_drv.c new file +7
-0+ unsigned long iova, int flags) +{ +DBG ("*** fault: iova=%08lx, flags=%d" , iova, flags); +return 0 ; +} + +int msm_register_iommu(struct drm_device *dev, struct iommu_domain*iommu)
Quoted 15 lines
drivers/gpu/drm/msm/msm_drv.c line 51
Quoted 12 lines
drivers/gpu/drm/msm/msm_drv.c new file +12
-0+{ +struct msm_drm_private *priv = dev->dev_private; +int idx = priv->num_iommus++; + +if (WARN_ON (idx >=ARRAY_SIZE (priv->iommus))) +return -EINVAL ; + + priv->iommus[idx] = iommu; + +iommu_set_fault_handler (iommu, msm_fault_handler); + +/* need to iommu_attach_device() somewhere?? on resume?? */ We are going to end up with 2 IOMMUs to deal with.
Oh, yeah, I did figure out the attach stuff eventually, but forgot to remove that note to myself.
Rough plan is that different initiators (display, gpu, etc) request iova in a particular device-space (msm_gem_get_iova()), and the gem object keeps track of the device address in each domain that it is mapped.
Well, I'm still thinking about the best way to deal with per-context address space for GPU. One easy way is just use same address space in each context (although only with buffers shared to that context being mapped). That should work ok-ish, at least for newer GPU's with large address space. But I'm not super concerned about getting that part right up-front, because it won't be visible in the user<->kernel ABI so it is something that can be changed later.
Quoted 185 lines
Quoted 185 lines
drivers/gpu/drm/msm/msm_drv.c new file +17
-0+return idx; +} + +#ifdef CONFIG_DRM_MSM_REGISTER_LOGGING +static bool reglog = false; +MODULE_PARM_DESC (reglog,"Enable register read/write logging" ); +module_param (reglog, bool,0600 ); +#else +#define reglog 0 +#endif + +void __iomem *msm_ioremap (struct device *dev,resource_size_t offset, +unsigned long size,const char *name) +{ +void __iomem *ptr =devm_ioremap_nocache (dev, offset, size); +if (reglog) + printk(KERN_DEBUG "IO:region %s %08x %08lx\n", name,(u32)ptr, size);
drivers/gpu/drm/msm/msm_drv.c new file +77
-0+return ptr; +} + +void msm_writel (u32 data,void __iomem *addr) +{ +if (reglog) +printk (KERN_DEBUG "IO:W %08x %08x\n" , (u32 )addr, data); +writel (data, addr); +} + +u32 msm_readl (const void __iomem *addr) +{ +u32 val =readl (addr); +if (reglog) +printk (KERN_ERR "IO:R %08x %08x\n" , (u32 )addr, val); +return val; +} + +/* +* DRM operations: +*/ + +static int msm_unload (struct drm_device *dev) +{ +struct msm_drm_private *priv = dev->dev_private; +struct msm_kms *kms = priv->kms; + +drm_kms_helper_poll_fini (dev); +drm_mode_config_cleanup (dev); +drm_vblank_cleanup (dev); + +pm_runtime_get_sync (dev->dev); +drm_irq_uninstall (dev); +pm_runtime_put_sync (dev->dev); + +flush_workqueue (priv->wq); +destroy_workqueue (priv->wq); + +if (kms) { +pm_runtime_disable (dev->dev); + kms->funcs->destroy (kms); + } + + dev->dev_private =NULL ; + +pm_runtime_disable (dev->dev); + +kfree (priv); + +return 0 ; +} + +static int msm_load (struct drm_device *dev,unsigned long flags) +{ +struct platform_device *pdev = dev->platformdev; +struct msm_drm_private *priv; +struct msm_kms *kms; +int ret; + + priv =kzalloc (sizeof (*priv),GFP_KERNEL ); +if (!priv) { +dev_err (dev->dev,"failed to allocate private data\n" ); +return -ENOMEM ; + } + + dev->dev_private = priv; + + priv->wq =alloc_ordered_workqueue ("msm" ,0 ); + +INIT_LIST_HEAD (&priv->obj_list); + +drm_mode_config_init (dev); + + kms =mdp4_kms_init (dev); +if (IS_ERR (kms)) { +/* +* NOTE: once we have GPU support, having no kms should not
+ * be considered fatal.. ideally we would still support
gpu
+ * and (for example) use dmabuf/prime to share buffers
withdrivers/gpu/drm/msm/msm_drv.c new file +14
-0+* imx drm driver on iMX5 +*/ +dev_err (dev->dev,"failed to load kms\n" ); + ret =PTR_ERR (priv->kms); + goto fail; + } + + priv->kms = kms; + +if (kms) { +pm_runtime_enable (dev->dev); + ret = kms->funcs->hw_init (kms); +if (ret) { + dev_err(dev->dev, "kms hw init failed: %d\n",ret);
drivers/gpu/drm/msm/msm_drv.c new file +60
-0+ goto fail; + } + } + + dev->mode_config.min_width =0 ; + dev->mode_config.min_height =0 ; + dev->mode_config.max_width =2048 ; + dev->mode_config.max_height =2048 ; + dev->mode_config.funcs = &mode_config_funcs; + + ret =drm_vblank_init (dev,1 ); +if (ret <0 ) { +dev_err (dev->dev,"failed to initialize vblank\n" ); + goto fail; + } + +pm_runtime_get_sync (dev->dev); + ret =drm_irq_install (dev); +pm_runtime_put_sync (dev->dev); +if (ret <0 ) { +dev_err (dev->dev,"failed to install IRQ handler\n" ); + goto fail; + } + +platform_set_drvdata (pdev, dev); + +#ifdef CONFIG_DRM_MSM_FBDEV + priv->fbdev =msm_fbdev_init (dev); +#endif + +drm_kms_helper_poll_init (dev); + +return 0 ; + +fail: +msm_unload (dev); +return ret; +} + +static void msm_preclose (struct drm_device *dev,struct drm_file *file) +{ +struct msm_drm_private *priv = dev->dev_private; +struct msm_kms *kms = priv->kms; +if (kms) + kms->funcs->preclose (kms, file); +} + +static void msm_lastclose (struct drm_device *dev) +{ +struct msm_drm_private *priv = dev->dev_private; +if (priv->fbdev) { +drm_modeset_lock_all (dev); +drm_fb_helper_restore_fbdev_mode (priv->fbdev); +drm_modeset_unlock_all (dev); + } +} + +static irqreturn_t msm_irq (DRM_IRQ_ARGS ) +{ +struct drm_device *dev = arg;
Quoted 8 lines
drivers/gpu/drm/msm/msm_drv.c line 226
Quoted 4 lines
drivers/gpu/drm/msm/msm_drv.c new file +4
-0+struct msm_drm_private *priv = dev->dev_private; +struct msm_kms *kms = priv->kms; +BUG_ON (!kms); +return kms->funcs->irq (kms);And we will have separate interrupts too - has anybody else had to deal with that (too lazy to check).
IIRC, exynos, and perhaps some others do..
This is actually already the case in msm kms code, since HDMI block has it's own irq (for HPD and DDC). My thinking here is that every "module" with interrupts not vblank related, that module can separately register register it's own handler. It is partly an arbitrary decision, but seemed to make sense to me, because DRM core doesn't really care too much about interrupts beyond vblank.
BR,
-R
Quoted 765 lines
Quoted 750 lines
drivers/gpu/drm/msm/msm_drv.c new file +152
-0+} + +static void msm_irq_preinstall (struct drm_device *dev) +{ +struct msm_drm_private *priv = dev->dev_private; +struct msm_kms *kms = priv->kms; +BUG_ON (!kms); + kms->funcs->irq_preinstall (kms); +} + +static int msm_irq_postinstall (struct drm_device *dev) +{ +struct msm_drm_private *priv = dev->dev_private; +struct msm_kms *kms = priv->kms; +BUG_ON (!kms); +return kms->funcs->irq_postinstall (kms); +} + +static void msm_irq_uninstall (struct drm_device *dev) +{ +struct msm_drm_private *priv = dev->dev_private; +struct msm_kms *kms = priv->kms; +BUG_ON (!kms); + kms->funcs->irq_uninstall (kms); +} + +static int msm_enable_vblank (struct drm_device *dev,int crtc_id) +{ +struct msm_drm_private *priv = dev->dev_private; +struct msm_kms *kms = priv->kms; +if (!kms) +return -ENXIO ; +DBG ("dev=%p, crtc=%d" , dev, crtc_id); +return kms->funcs->enable_vblank (kms, priv->crtcs[crtc_id]); +} + +static void msm_disable_vblank (struct drm_device *dev,int crtc_id) +{ +struct msm_drm_private *priv = dev->dev_private; +struct msm_kms *kms = priv->kms; +if (!kms) +return ; +DBG ("dev=%p, crtc=%d" , dev, crtc_id); + kms->funcs->disable_vblank (kms, priv->crtcs[crtc_id]); +} + +#ifdef CONFIG_DEBUG_FS +static int msm_gem_show (struct seq_file *m,void *arg) +{ +struct drm_info_node *node = (struct drm_info_node *) m->private; +struct drm_device *dev = node->minor->dev; +struct msm_drm_private *priv = dev->dev_private; +int ret; + + ret =mutex_lock_interruptible (&dev->struct_mutex); +if (ret) +return ret; + +seq_printf (m,"All Objects:\n" ); +msm_gem_describe_objects (&priv->obj_list, m); + +mutex_unlock (&dev->struct_mutex); + +return 0 ; +} + +static int msm_mm_show (struct seq_file *m,void *arg) +{ +struct drm_info_node *node = (struct drm_info_node *) m->private; +struct drm_device *dev = node->minor->dev; +return drm_mm_dump_table (m, dev->mm_private); +} + +static int msm_fb_show (struct seq_file *m,void *arg) +{ +struct drm_info_node *node = (struct drm_info_node *) m->private; +struct drm_device *dev = node->minor->dev; +struct msm_drm_private *priv = dev->dev_private; +struct drm_framebuffer *fb, *fbdev_fb =NULL ; + +if (priv->fbdev) { +seq_printf (m,"fbcon " ); + fbdev_fb = priv->fbdev->fb; +msm_framebuffer_describe (fbdev_fb, m); + } + +mutex_lock (&dev->mode_config.fb_lock); +list_for_each_entry (fb, &dev->mode_config.fb_list, head) { +if (fb == fbdev_fb) +continue ; + +seq_printf (m,"user " ); +msm_framebuffer_describe (fb, m); + } +mutex_unlock (&dev->mode_config.fb_lock); + +return 0 ; +} + +static struct drm_info_list msm_debugfs_list[] = { + {"gem" , msm_gem_show,0 }, + {"mm" , msm_mm_show,0 }, + {"fb" , msm_fb_show,0 }, +}; + +static int msm_debugfs_init (struct drm_minor *minor) +{ +struct drm_device *dev = minor->dev; +int ret; + + ret =drm_debugfs_create_files (msm_debugfs_list, +ARRAY_SIZE (msm_debugfs_list), + minor->debugfs_root, minor); + +if (ret) { +dev_err (dev->dev,"could not install msm_debugfs_list\n" ); +return ret; + } + +return ret; +} + +static void msm_debugfs_cleanup (struct drm_minor *minor) +{ +drm_debugfs_remove_files (msm_debugfs_list, +ARRAY_SIZE (msm_debugfs_list), minor); +} +#endif + +static const struct vm_operations_struct vm_ops = { + .fault = msm_gem_fault, + .open = drm_gem_vm_open, + .close = drm_gem_vm_close, +}; + +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, + .fasync = drm_fasync, + .llseek = no_llseek, + .mmap = msm_gem_mmap, +}; + +static struct drm_driver msm_driver = { + .driver_features = DRIVER_HAVE_IRQ | DRIVER_GEM |
DRIVER_MODESET,drivers/gpu/drm/msm/msm_drv.c new file +113
-0+ .load = msm_load, + .unload = msm_unload, + .preclose = msm_preclose, + .lastclose = msm_lastclose, + .irq_handler = msm_irq, + .irq_preinstall = msm_irq_preinstall, + .irq_postinstall = msm_irq_postinstall, + .irq_uninstall = msm_irq_uninstall, + .get_vblank_counter = drm_vblank_count, + .enable_vblank = msm_enable_vblank, + .disable_vblank = msm_disable_vblank, + .gem_free_object = msm_gem_free_object, + .gem_vm_ops = &vm_ops, + .dumb_create = msm_gem_dumb_create, + .dumb_map_offset = msm_gem_dumb_map_offset, + .dumb_destroy = msm_gem_dumb_destroy, +#ifdef CONFIG_DEBUG_FS + .debugfs_init = msm_debugfs_init, + .debugfs_cleanup = msm_debugfs_cleanup, +#endif + .fops = &fops, + .name = "msm", + .desc = "MSM Snapdragon DRM", + .date = "20130625", + .major = 1, + .minor = 0, +}; + +#ifdef CONFIG_PM_SLEEP +static int msm_pm_suspend (struct device *dev) +{ +struct drm_device *ddev =dev_get_drvdata (dev); +struct msm_drm_private *priv = ddev->dev_private; +struct msm_kms *kms = priv->kms; + +drm_kms_helper_poll_disable (ddev); + +return kms->funcs->pm_suspend (kms); +} + +static int msm_pm_resume (struct device *dev) +{ +struct drm_device *ddev =dev_get_drvdata (dev); +struct msm_drm_private *priv = ddev->dev_private; +struct msm_kms *kms = priv->kms; +int ret =0 ; + + ret = kms->funcs->pm_resume (kms); +if (ret) +return ret; + +drm_kms_helper_poll_enable (ddev); + +return 0 ; +} +#endif + +static const struct dev_pm_ops msm_pm_ops = { +SET_SYSTEM_SLEEP_PM_OPS (msm_pm_suspend, msm_pm_resume) +}; + +/* +* Platform driver: +*/ + +static int msm_pdev_probe (struct platform_device *pdev) +{ +return drm_platform_init (&msm_driver, pdev); +} + +static int msm_pdev_remove (struct platform_device *pdev) +{ +drm_platform_exit (&msm_driver, pdev); + +return 0 ; +} + +static const struct platform_device_id msm_id[] = { + {"mdp" ,0 }, + { } +}; + +static struct platform_driver msm_platform_driver = { + .probe = msm_pdev_probe, + .remove = msm_pdev_remove, + .driver = { + .owner =THIS_MODULE , + .name ="msm" , + .pm = &msm_pm_ops, + }, + .id_table = msm_id, +}; + +static int __init msm_drm_init (void ) +{ +DBG ("init" ); +hdmi_init (); +return platform_driver_register (&msm_platform_driver); +} + +static void __exit msm_drm_fini(void) +{ +DBG ("fini" ); +platform_driver_unregister (&msm_platform_driver); +hdmi_fini (); +} + +module_init (msm_drm_init); +module_exit (msm_drm_fini); + +MODULE_AUTHOR ("Rob Clark <[email protected]" ); +MODULE_DESCRIPTION ("MSM DRM Driver" ); +MODULE_LICENSE ("GPL" );drivers/gpu/drm/msm/msm_gem.c new file +5
-0@@ -0,0 +1,441 @@ +/* +* Copyright (C) 2013 Red Hat +* Author: Rob Clark <[email protected]> +* +* This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */drivers/gpu/drm/msm/msm_gem.c new file +87
-0+ +#include <linux/spinlock.h> +#include <linux/shmem_fs.h> + +#include "msm_drv.h" + +struct msm_gem_object { +struct drm_gem_object base; + +struct list_head mm_list; + +uint32_t flags; +struct page **pages; +struct sg_table *sgt; +void *vaddr; + +struct { +// XXX +uint32_t iova; + } domain[NUM_DOMAINS ]; +}; +#define to_msm_bo (x) container_of(x, struct msm_gem_object, base) + +/* called with dev->struct_mutex held */ +/* TODO move this into drm_gem.c */ +static struct page **attach_pages (struct drm_gem_object *obj) +{ +struct inode *inode; +struct address_space *mapping; +struct page *p, **pages; +int i, npages; + +/* This is the shared memory object that backs the GEM resource */ + inode =file_inode (obj->filp); + mapping = inode->i_mapping; + + npages = obj->size >>PAGE_SHIFT ; + + pages =drm_malloc_ab (npages,sizeof (struct page *)); +if (pages ==NULL ) +return ERR_PTR (-ENOMEM ); + +for (i =0 ; i < npages; i++) { + p =shmem_read_mapping_page (mapping, i); +if (IS_ERR (p)) + goto fail; + pages[i] = p; + } + +return pages; + +fail: +while (i--) +page_cache_release (pages[i]); + +drm_free_large (pages); +return ERR_CAST (p); +} + +static void detach_pages (struct drm_gem_object *obj,struct page **pages) +{ +int i, npages; + + npages = obj->size >>PAGE_SHIFT ; + +for (i =0 ; i < npages; i++) { +set_page_dirty (pages[i]); + +/* Undo the reference we took when populating the table */ +page_cache_release (pages[i]); + } + +drm_free_large (pages); +} + + +/* called with dev->struct_mutex held */ +static struct page **get_pages (struct drm_gem_object *obj) +{ +struct msm_gem_object *msm_obj =to_msm_bo (obj); + +if (!msm_obj->pages) { +struct page **p =attach_pages (obj); +int npages = obj->size >>PAGE_SHIFT ; + +if (IS_ERR (p)) { + dev_err(obj->dev->dev, "could not get pages:%ld\n",
drivers/gpu/drm/msm/msm_gem.c new file +34
-0+ PTR_ERR(p)); +return p; + } + msm_obj->pages = p; + msm_obj->sgt =drm_prime_pages_to_sg (p, npages); + } + +return msm_obj->pages; +} + +static void put_pages (struct drm_gem_object *obj) +{ +struct msm_gem_object *msm_obj =to_msm_bo (obj); + +if (!msm_obj->pages) { +if (msm_obj->sgt) { +sg_free_table (msm_obj->sgt); +kfree (msm_obj->sgt); + } +detach_pages (obj, msm_obj->pages); + msm_obj->pages =NULL ; + } +} + +int msm_gem_mmap_obj (struct drm_gem_object *obj, +struct vm_area_struct *vma) +{ +struct msm_gem_object *msm_obj =to_msm_bo (obj); + + vma->vm_flags &= ~VM_PFNMAP ; + vma->vm_flags |=VM_MIXEDMAP ; + +if (msm_obj->flags &MSM_BO_WC ) { + vma->vm_page_prot =pgprot_writecombine(vm_get_page_prot(vma->vm_flags));
drivers/gpu/drm/msm/msm_gem.c new file +2
-0+ }else if (msm_obj->flags &MSM_BO_UNCACHED ) { + vma->vm_page_prot =pgprot_noncached(vm_get_page_prot(vma->vm_flags));
drivers/gpu/drm/msm/msm_gem.c new file +3
-0+ }else { +/* +* Shunt off cached objs to shmem file so they have their own
+ *address_space(sounmap_mapping_rangedoes what we
want,drivers/gpu/drm/msm/msm_gem.c new file +37
-0+* in particular in the case of mmap'd dmabufs) +*/ +fput (vma->vm_file); +get_file (obj->filp); + vma->vm_pgoff =0 ; + vma->vm_file = obj->filp; + + vma->vm_page_prot =vm_get_page_prot (vma->vm_flags); + } + +return 0 ; +} + +int msm_gem_mmap (struct file *filp,struct vm_area_struct *vma) +{ +int ret; + + ret =drm_gem_mmap (filp, vma); +if (ret) { +DBG ("mmap failed: %d" , ret); +return ret; + } + +return msm_gem_mmap_obj (vma->vm_private_data, vma); +} + +int msm_gem_fault (struct vm_area_struct *vma,struct vm_fault *vmf) +{ +struct drm_gem_object *obj = vma->vm_private_data; +struct msm_gem_object *msm_obj =to_msm_bo (obj); +struct drm_device *dev = obj->dev; +struct page **pages; +unsigned long pfn; +pgoff_t pgoff; +int ret; + +/* Make sure we don't parallel update on a fault, nor move or remove
drivers/gpu/drm/msm/msm_gem.c new file +21
-0+* something from beneath our feet +*/ +mutex_lock (&dev->struct_mutex); + +/* make sure we have pages attached now */ + pages =get_pages (obj); +if (IS_ERR (pages)) { + ret =PTR_ERR (pages); + goto out; + } + +/* We don't use vmf->pgoff since that has the fake offset: */ + pgoff = ((unsigned long )vmf->virtual_address - + vma->vm_start) >>PAGE_SHIFT ; + + pfn =page_to_pfn (msm_obj->pages[pgoff]); + +VERB ("Inserting %p pfn %lx, pa %lx" , vmf->virtual_address, + pfn, pfn <<PAGE_SHIFT ); + + ret = vm_insert_mixed(vma, (unsigned long)vmf->virtual_address,pfn);
drivers/gpu/drm/msm/msm_gem.c new file +28
-0+ +out: +mutex_unlock (&dev->struct_mutex); +switch (ret) { +case 0 : +case -ERESTARTSYS : +case -EINTR : +return VM_FAULT_NOPAGE ; +case -ENOMEM : +return VM_FAULT_OOM ; +default : +return VM_FAULT_SIGBUS ; + } +} + +/** get mmap offset */ +static uint64_t mmap_offset (struct drm_gem_object *obj) +{ +struct drm_device *dev = obj->dev; + +WARN_ON (!mutex_is_locked (&dev->struct_mutex)); + +if (!obj->map_list.map) { +/* Make it mmapable */ +int ret =drm_gem_create_mmap_offset (obj); + +if (ret) { + dev_err(dev->dev, "could not allocate mmapoffset\n");
drivers/gpu/drm/msm/msm_gem.c new file +55
-0+return 0 ; + } + } + +return (uint64_t )obj->map_list.hash.key <<PAGE_SHIFT ; +} + +uint64_t msm_gem_mmap_offset (struct drm_gem_object *obj) +{ +uint64_t offset; +mutex_lock (&obj->dev->struct_mutex); + offset =mmap_offset (obj); +mutex_unlock (&obj->dev->struct_mutex); +return offset; +} + +int msm_gem_get_iova (struct drm_gem_object *obj,int id,uint32_t *iova) +{ +struct msm_gem_object *msm_obj =to_msm_bo (obj); +int ret =0 ; + +mutex_lock (&obj->dev->struct_mutex); +if (!msm_obj->domain[id].iova) { +struct msm_drm_private *priv = obj->dev->dev_private; +uint32_t offset = (uint32_t )mmap_offset (obj); +get_pages (obj); + ret =iommu_map_range (priv->iommus[id], offset, + msm_obj->sgt->sgl, obj->size,IOMMU_READ ); + msm_obj->domain[id].iova = offset; + } +mutex_unlock (&obj->dev->struct_mutex); + +if (!ret) + *iova = msm_obj->domain[id].iova; + +return ret; +} + +void msm_gem_put_iova (struct drm_gem_object *obj,int id) +{ +} + +int msm_gem_dumb_create (struct drm_file *file,struct drm_device *dev, +struct drm_mode_create_dumb *args) +{ + args->pitch =align_pitch (args->width, args->bpp); + args->size =PAGE_ALIGN (args->pitch * args->height); +return msm_gem_new_handle (dev, file, args->size, +MSM_BO_SCANOUT |MSM_BO_WC , &args->handle); +} + +int msm_gem_dumb_destroy (struct drm_file *file,struct drm_device *dev, +uint32_t handle) +{ +/* No special work needed, drop the reference and see what falls out */
drivers/gpu/drm/msm/msm_gem.c new file +4
-0+return drm_gem_handle_delete (file, handle); +} + +int msm_gem_dumb_map_offset(struct drm_file *file, struct drm_device*dev,
drivers/gpu/drm/msm/msm_gem.c new file +48
-0+ uint32_t handle, uint64_t *offset) +{ + struct drm_gem_object *obj; +int ret =0 ; + +/* GEM does all our handle to object mapping */ + obj =drm_gem_object_lookup (dev, file, handle); +if (obj ==NULL ) { + ret = -ENOENT ; + goto fail; + } + + *offset =msm_gem_mmap_offset (obj); + +drm_gem_object_unreference_unlocked (obj); + +fail: +return ret; +} + +void *msm_gem_vaddr (struct drm_gem_object *obj) +{ +struct msm_gem_object *msm_obj =to_msm_bo (obj); +WARN_ON (!mutex_is_locked (&obj->dev->struct_mutex)); +if (!msm_obj->vaddr) { +struct page **pages =get_pages (obj); +if (IS_ERR (pages)) +return ERR_CAST (pages); + msm_obj->vaddr =vmap (pages, obj->size >>PAGE_SHIFT , +VM_MAP ,pgprot_writecombine (PAGE_KERNEL )); + } +return msm_obj->vaddr; +} + +#ifdef CONFIG_DEBUG_FS +void msm_gem_describe (struct drm_gem_object *obj,struct seq_file *m) +{ +struct drm_device *dev = obj->dev; +struct msm_gem_object *msm_obj =to_msm_bo (obj); +uint64_t off =0 ; + +WARN_ON (!mutex_is_locked (&dev->struct_mutex)); + +if (obj->map_list.map) + off = (uint64_t )obj->map_list.hash.key; + + seq_printf(m, "%08x: %2d (%2d) %08llx %p %d\n", + msm_obj->flags, obj->name,
obj->refcount.refcount.counter,drivers/gpu/drm/msm/msm_gem.c new file +40
-0+ off, msm_obj->vaddr, obj->size); +} + +void msm_gem_describe_objects (struct list_head *list,struct seq_file *m) +{ +struct msm_gem_object *msm_obj; +int count =0 ; +size_t size =0 ; + +list_for_each_entry (msm_obj, list, mm_list) { +struct drm_gem_object *obj = &msm_obj->base; +seq_printf (m," " ); +msm_gem_describe (obj, m); + count++; + size += obj->size; + } + +seq_printf (m,"Total %d objects, %zu bytes\n" , count, size); +} +#endif + +void msm_gem_free_object (struct drm_gem_object *obj) +{ +struct drm_device *dev = obj->dev; +struct msm_gem_object *msm_obj =to_msm_bo (obj); +int id; + +WARN_ON (!mutex_is_locked (&dev->struct_mutex)); + +list_del (&msm_obj->mm_list); + +if (obj->map_list.map) +drm_gem_free_mmap_offset (obj); + +if (msm_obj->vaddr) +vunmap (msm_obj->vaddr); + +for (id =0 ; id <ARRAY_SIZE (msm_obj->domain); id++) { +if (msm_obj->domain[id].iova) { + struct msm_drm_private *priv =
obj->dev->dev_private;drivers/gpu/drm/msm/msm_gem.c new file +2
-0+uint32_t offset = (uint32_t )mmap_offset (obj); + iommu_unmap_range(priv->iommus[id], offset,
obj->size);drivers/gpu/drm/msm/msm_gem.c new file +11
-0+ } + } + +put_pages (obj); + +drm_gem_object_release (obj); + +kfree (obj); +} + +/* convenience method to construct a GEM buffer object, and userspace handle */
drivers/gpu/drm/msm/msm_gem.c new file +43
-0+int msm_gem_new_handle (struct drm_device *dev,struct drm_file *file, +uint32_t size,uint32_t flags,uint32_t *handle) +{ +struct drm_gem_object *obj; +int ret; + + obj =msm_gem_new (dev, size, flags); +if (!obj) +return -ENOMEM ; + + ret =drm_gem_handle_create (file, obj, handle); + +/* drop reference from allocate - handle holds it now */ +drm_gem_object_unreference_unlocked (obj); + +return ret; +} + +struct drm_gem_object *msm_gem_new (struct drm_device *dev, +uint32_t size,uint32_t flags) +{ +struct msm_drm_private *priv = dev->dev_private; +struct msm_gem_object *msm_obj; +struct drm_gem_object *obj =NULL ; +int ret; + + size =PAGE_ALIGN (size); + + msm_obj =kzalloc (sizeof (*msm_obj),GFP_KERNEL ); +if (!msm_obj) + goto fail; + + obj = &msm_obj->base; + + ret =drm_gem_object_init (dev, obj, size); +if (ret) + goto fail; + + msm_obj->flags = flags; + +mutex_lock (&obj->dev->struct_mutex); +list_add (&msm_obj->mm_list, &priv->obj_list); +mutex_unlock (&obj->dev->struct_mutex);drivers/gpu/drm/msm/msm_gem.c line 441
Quoted 10 lines
drivers/gpu/drm/msm/msm_gem.c new file +9
-0+ +return obj; + +fail: +if (obj) +drm_gem_object_unreference_unlocked (obj); + +return NULL ; +}Yay GEM. No complaints here.
Jordan