diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2023-08-30 13:34:34 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2023-08-30 13:34:34 -0700 |
commit | 461f35f014466c4e26dca6be0f431f57297df3f2 (patch) | |
tree | 0bd2fded69ba0752ca16c304d3e1880d5f1eb30b /drivers/gpu/drm/ast/ast_main.c | |
parent | 53ea7f624fb91074c2f9458832ed74975ee5d64c (diff) | |
parent | 3698a75f5a98d0a6599e2878ab25d30a82dd836a (diff) |
Merge tag 'drm-next-2023-08-30' of git://anongit.freedesktop.org/drm/drm
Pull drm updates from Dave Airlie:
"The drm core grew a new generic gpu virtual address manager, and new
execution locking helpers. These are used by nouveau now to provide
uAPI support for the userspace Vulkan driver. AMD had a bunch of new
IP core support, loads of refactoring around fbdev, but mostly just
the usual amount of stuff across the board.
core:
- fix gfp flags in drmm_kmalloc
gpuva:
- add new generic GPU VA manager (for nouveau initially)
syncobj:
- add new DRM_IOCTL_SYNCOBJ_EVENTFD ioctl
dma-buf:
- acquire resv lock for mmap() in exporters
- support dma-buf self import automatically
- docs fixes
backlight:
- fix fbdev interactions
atomic:
- improve logging
prime:
- remove struct gem_prim_mmap plus driver updates
gem:
- drm_exec: add locking over multiple GEM objects
- fix lockdep checking
fbdev:
- make fbdev userspace interfaces optional
- use linux device instead of fbdev device
- use deferred i/o helper macros in various drivers
- Make FB core selectable without drivers
- Remove obsolete flags FBINFO_DEFAULT and FBINFO_FLAG_DEFAULT
- Add helper macros and Kconfig tokens for DMA-allocated framebuffer
ttm:
- support init_on_free
- swapout fixes
panel:
- panel-edp: Support AUO B116XAB01.4
- Support Visionox R66451 plus DT bindings
- ld9040:
- Backlight support
- magic improved
- Kconfig fix
- Convert to of_device_get_match_data()
- Fix Kconfig dependencies
- simple:
- Set bpc value to fix warning
- Set connector type for AUO T215HVN01
- Support Innolux G156HCE-L01 plus DT bindings
- ili9881: Support TDO TL050HDV35 LCD panel plus DT bindings
- startek: Support KD070FHFID015 MIPI-DSI panel plus DT bindings
- sitronix-st7789v:
- Support Inanbo T28CP45TN89 plus DT bindings
- Support EDT ET028013DMA plus DT bindings
- Various cleanups
- edp: Add timings for N140HCA-EAC
- Allow panels and touchscreens to power sequence together
- Fix Innolux G156HCE-L01 LVDS clock
bridge:
- debugfs for chains support
- dw-hdmi:
- Improve support for YUV420 bus format
- CEC suspend/resume
- update EDID on HDMI detect
- dw-mipi-dsi: Fix enable/disable of DSI controller
- lt9611uxc: Use MODULE_FIRMWARE()
- ps8640: Remove broken EDID code
- samsung-dsim: Fix command transfer
- tc358764:
- Handle HS/VS polarity
- Use BIT() macro
- Various cleanups
- adv7511: Fix low refresh rate
- anx7625:
- Switch to macros instead of hardcoded values
- locking fixes
- tc358767: fix hardware delays
- sitronix-st7789v:
- Support panel orientation
- Support rotation property
- Add support for Jasonic JT240MHQS-HWT-EK-E3 plus DT bindings
amdgpu:
- SDMA 6.1.0 support
- HDP 6.1 support
- SMUIO 14.0 support
- PSP 14.0 support
- IH 6.1 support
- Lots of checkpatch cleanups
- GFX 9.4.3 updates
- Add USB PD and IFWI flashing documentation
- GPUVM updates
- RAS fixes
- DRR fixes
- FAMS fixes
- Virtual display fixes
- Soft IH fixes
- SMU13 fixes
- Rework PSP firmware loading for other IPs
- Kernel doc fixes
- DCN 3.0.1 fixes
- LTTPR fixes
- DP MST fixes
- DCN 3.1.6 fixes
- SMU 13.x fixes
- PSP 13.x fixes
- SubVP fixes
- GC 9.4.3 fixes
- Display bandwidth calculation fixes
- VCN4 secure submission fixes
- Allow building DC on RISC-V
- Add visible FB info to bo_print_info
- HBR3 fixes
- GFX9 MCBP fix
- GMC10 vmhub index fix
- GMC11 vmhub index fix
- Create a new doorbell manager
- SR-IOV fixes
- initial freesync panel replay support
- revert zpos properly until igt regression is fixeed
- use TTM to manage doorbell BAR
- Expose both current and average power via hwmon if supported
amdkfd:
- Cleanup CRIU dma-buf handling
- Use KIQ to unmap HIQ
- GFX 9.4.3 debugger updates
- GFX 9.4.2 debugger fixes
- Enable cooperative groups fof gfx11
- SVM fixes
- Convert older APUs to use dGPU path like newer APUs
- Drop IOMMUv2 path as it is no longer used
- TBA fix for aldebaran
i915:
- ICL+ DSI modeset sequence
- HDCP improvements
- MTL display fixes and cleanups
- HSW/BDW PSR1 restored
- Init DDI ports in VBT order
- General display refactors
- Start using plane scale factor for relative data rate
- Use shmem for dpt objects
- Expose RPS thresholds in sysfs
- Apply GuC SLPC min frequency softlimit correctly
- Extend Wa_14015795083 to TGL, RKL, DG1 and ADL
- Fix a VMA UAF for multi-gt platform
- Do not use stolen on MTL due to HW bug
- Check HuC and GuC version compatibility on MTL
- avoid infinite GPU waits due to premature release of request memory
- Fixes and updates for GSC memory allocation
- Display SDVO fixes
- Take stolen handling out of FBC code
- Make i915_coherent_map_type GT-centric
- Simplify shmem_create_from_object map_type
msm:
- SM6125 MDSS support
- DPU: SM6125 DPU support
- DSI: runtime PM support, burst mode support
- DSI PHY: SM6125 support in 14nm DSI PHY driver
- GPU: prepare for a7xx
- fix a690 firmware
- disable relocs on a6xx and newer
radeon:
- Lots of checkpatch cleanups
ast:
- improve device-model detection
- Represent BMV as virtual connector
- Report DP connection status
nouveau:
- add new exec/bind interface to support Vulkan
- document some getparam ioctls
- improve VRAM detection
- various fixes/cleanups
- workraound DPCD issues
ivpu:
- MMU updates
- debugfs support
- Support vpu4
virtio:
- add sync object support
atmel-hlcdc:
- Support inverted pixclock polarity
etnaviv:
- runtime PM cleanups
- hang handling fixes
exynos:
- use fbdev DMA helpers
- fix possible NULL ptr dereference
komeda:
- always attach encoder
omapdrm:
- use fbdev DMA helpers
ingenic:
- kconfig regmap fixes
loongson:
- support display controller
mediatek:
- Small mtk-dpi cleanups
- DisplayPort: support eDP and aux-bus
- Fix coverity issues
- Fix potential memory leak if vmap() fail
mgag200:
- minor fixes
mxsfb:
- support disabling overlay planes
panfrost:
- fix sync in IRQ handling
ssd130x:
- Support per-controller default resolution plus DT bindings
- Reduce memory-allocation overhead
- Improve intermediate buffer size computation
- Fix allocation of temporary buffers
- Fix pitch computation
- Fix shadow plane allocation
tegra:
- use fbdev DMA helpers
- Convert to devm_platform_ioremap_resource()
- support bridge/connector
- enable PM
tidss:
- Support TI AM625 plus DT bindings
- Implement new connector model plus driver updates
vkms:
- improve write back support
- docs fixes
- support gamma LUT
zynqmp-dpsub:
- misc fixes"
* tag 'drm-next-2023-08-30' of git://anongit.freedesktop.org/drm/drm: (1327 commits)
drm/gpuva_mgr: remove unused prev pointer in __drm_gpuva_sm_map()
drm/tests/drm_kunit_helpers: Place correct function name in the comment header
drm/nouveau: uapi: don't pass NO_PREFETCH flag implicitly
drm/nouveau: uvmm: fix unset region pointer on remap
drm/nouveau: sched: avoid job races between entities
drm/i915: Fix HPD polling, reenabling the output poll work as needed
drm: Add an HPD poll helper to reschedule the poll work
drm/i915: Fix TLB-Invalidation seqno store
drm/ttm/tests: Fix type conversion in ttm_pool_test
drm/msm/a6xx: Bail out early if setting GPU OOB fails
drm/msm/a6xx: Move LLC accessors to the common header
drm/msm/a6xx: Introduce a6xx_llc_read
drm/ttm/tests: Require MMU when testing
drm/panel: simple: Fix Innolux G156HCE-L01 LVDS clock
Revert "Revert "drm/amdgpu/display: change pipe policy for DCN 2.0""
drm/amdgpu: Add memory vendor information
drm/amd: flush any delayed gfxoff on suspend entry
drm/amdgpu: skip fence GFX interrupts disable/enable for S0ix
drm/amdgpu: Remove gfxoff check in GFX v9.4.3
drm/amd/pm: Update pci link speed for smu v13.0.6
...
Diffstat (limited to 'drivers/gpu/drm/ast/ast_main.c')
-rw-r--r-- | drivers/gpu/drm/ast/ast_main.c | 298 |
1 files changed, 167 insertions, 131 deletions
diff --git a/drivers/gpu/drm/ast/ast_main.c b/drivers/gpu/drm/ast/ast_main.c index 1f35438f614a..dae365ed3969 100644 --- a/drivers/gpu/drm/ast/ast_main.c +++ b/drivers/gpu/drm/ast/ast_main.c @@ -35,131 +35,153 @@ #include "ast_drv.h" -void ast_set_index_reg_mask(struct ast_device *ast, - uint32_t base, uint8_t index, - uint8_t mask, uint8_t val) +static bool ast_is_vga_enabled(struct drm_device *dev) { - u8 tmp; - ast_io_write8(ast, base, index); - tmp = (ast_io_read8(ast, base + 1) & mask) | val; - ast_set_index_reg(ast, base, index, tmp); + struct ast_device *ast = to_ast_device(dev); + u8 ch; + + ch = ast_io_read8(ast, AST_IO_VGA_ENABLE_PORT); + + return !!(ch & 0x01); } -uint8_t ast_get_index_reg(struct ast_device *ast, - uint32_t base, uint8_t index) +static void ast_enable_vga(struct drm_device *dev) { - uint8_t ret; - ast_io_write8(ast, base, index); - ret = ast_io_read8(ast, base + 1); - return ret; + struct ast_device *ast = to_ast_device(dev); + + ast_io_write8(ast, AST_IO_VGA_ENABLE_PORT, 0x01); + ast_io_write8(ast, AST_IO_MISC_PORT_WRITE, 0x01); } -uint8_t ast_get_index_reg_mask(struct ast_device *ast, - uint32_t base, uint8_t index, uint8_t mask) +/* + * Run this function as part of the HW device cleanup; not + * when the DRM device gets released. + */ +static void ast_enable_mmio_release(void *data) { - uint8_t ret; - ast_io_write8(ast, base, index); - ret = ast_io_read8(ast, base + 1) & mask; - return ret; + struct ast_device *ast = data; + + /* enable standard VGA decode */ + ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0xa1, 0x04); } -static void ast_detect_config_mode(struct drm_device *dev, u32 *scu_rev) +static int ast_enable_mmio(struct ast_device *ast) { - struct device_node *np = dev->dev->of_node; - struct ast_device *ast = to_ast_device(dev); + struct drm_device *dev = &ast->base; + + ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0xa1, 0x06); + + return devm_add_action_or_reset(dev->dev, ast_enable_mmio_release, ast); +} + +static void ast_open_key(struct ast_device *ast) +{ + ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0x80, 0xA8); +} + +static int ast_device_config_init(struct ast_device *ast) +{ + struct drm_device *dev = &ast->base; struct pci_dev *pdev = to_pci_dev(dev->dev); - uint32_t data, jregd0, jregd1; + struct device_node *np = dev->dev->of_node; + uint32_t scu_rev = 0xffffffff; + u32 data; + u8 jregd0, jregd1; + + /* + * Find configuration mode and read SCU revision + */ - /* Defaults */ ast->config_mode = ast_use_defaults; - *scu_rev = 0xffffffff; /* Check if we have device-tree properties */ - if (np && !of_property_read_u32(np, "aspeed,scu-revision-id", - scu_rev)) { + if (np && !of_property_read_u32(np, "aspeed,scu-revision-id", &data)) { /* We do, disable P2A access */ ast->config_mode = ast_use_dt; - drm_info(dev, "Using device-tree for configuration\n"); - return; - } + scu_rev = data; + } else if (pdev->device == PCI_CHIP_AST2000) { // Not all families have a P2A bridge + /* + * The BMC will set SCU 0x40 D[12] to 1 if the P2 bridge + * is disabled. We force using P2A if VGA only mode bit + * is set D[7] + */ + jregd0 = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xd0, 0xff); + jregd1 = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xd1, 0xff); + if (!(jregd0 & 0x80) || !(jregd1 & 0x10)) { + + /* + * We have a P2A bridge and it is enabled. + */ + + /* Patch AST2500/AST2510 */ + if ((pdev->revision & 0xf0) == 0x40) { + if (!(jregd0 & AST_VRAM_INIT_STATUS_MASK)) + ast_patch_ahb_2500(ast); + } - /* Not all families have a P2A bridge */ - if (pdev->device != PCI_CHIP_AST2000) - return; + /* Double check that it's actually working */ + data = ast_read32(ast, 0xf004); + if ((data != 0xffffffff) && (data != 0x00)) { + ast->config_mode = ast_use_p2a; - /* - * The BMC will set SCU 0x40 D[12] to 1 if the P2 bridge - * is disabled. We force using P2A if VGA only mode bit - * is set D[7] - */ - jregd0 = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xd0, 0xff); - jregd1 = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xd1, 0xff); - if (!(jregd0 & 0x80) || !(jregd1 & 0x10)) { - /* Patch AST2500 */ - if (((pdev->revision & 0xF0) == 0x40) - && ((jregd0 & AST_VRAM_INIT_STATUS_MASK) == 0)) - ast_patch_ahb_2500(ast); - - /* Double check it's actually working */ - data = ast_read32(ast, 0xf004); - if ((data != 0xFFFFFFFF) && (data != 0x00)) { - /* P2A works, grab silicon revision */ - ast->config_mode = ast_use_p2a; - - drm_info(dev, "Using P2A bridge for configuration\n"); - - /* Read SCU7c (silicon revision register) */ - ast_write32(ast, 0xf004, 0x1e6e0000); - ast_write32(ast, 0xf000, 0x1); - *scu_rev = ast_read32(ast, 0x1207c); - return; + /* Read SCU7c (silicon revision register) */ + ast_write32(ast, 0xf004, 0x1e6e0000); + ast_write32(ast, 0xf000, 0x1); + scu_rev = ast_read32(ast, 0x1207c); + } } } - /* We have a P2A bridge but it's disabled */ - drm_info(dev, "P2A bridge disabled, using default configuration\n"); -} - -static int ast_detect_chip(struct drm_device *dev, bool *need_post) -{ - struct ast_device *ast = to_ast_device(dev); - struct pci_dev *pdev = to_pci_dev(dev->dev); - uint32_t jreg, scu_rev; + switch (ast->config_mode) { + case ast_use_defaults: + drm_info(dev, "Using default configuration\n"); + break; + case ast_use_dt: + drm_info(dev, "Using device-tree for configuration\n"); + break; + case ast_use_p2a: + drm_info(dev, "Using P2A bridge for configuration\n"); + break; + } /* - * If VGA isn't enabled, we need to enable now or subsequent - * access to the scratch registers will fail. We also inform - * our caller that it needs to POST the chip - * (Assumption: VGA not enabled -> need to POST) + * Identify chipset */ - if (!ast_is_vga_enabled(dev)) { - ast_enable_vga(dev); - drm_info(dev, "VGA not enabled on entry, requesting chip POST\n"); - *need_post = true; - } else - *need_post = false; - - - /* Enable extended register access */ - ast_open_key(ast); - ast_enable_mmio(dev); - /* Find out whether P2A works or whether to use device-tree */ - ast_detect_config_mode(dev, &scu_rev); - - /* Identify chipset */ if (pdev->revision >= 0x50) { ast->chip = AST2600; drm_info(dev, "AST 2600 detected\n"); } else if (pdev->revision >= 0x40) { - ast->chip = AST2500; - drm_info(dev, "AST 2500 detected\n"); + switch (scu_rev & 0x300) { + case 0x0100: + ast->chip = AST2510; + drm_info(dev, "AST 2510 detected\n"); + break; + default: + ast->chip = AST2500; + drm_info(dev, "AST 2500 detected\n"); + } } else if (pdev->revision >= 0x30) { - ast->chip = AST2400; - drm_info(dev, "AST 2400 detected\n"); + switch (scu_rev & 0x300) { + case 0x0100: + ast->chip = AST1400; + drm_info(dev, "AST 1400 detected\n"); + break; + default: + ast->chip = AST2400; + drm_info(dev, "AST 2400 detected\n"); + } } else if (pdev->revision >= 0x20) { - ast->chip = AST2300; - drm_info(dev, "AST 2300 detected\n"); + switch (scu_rev & 0x300) { + case 0x0000: + ast->chip = AST1300; + drm_info(dev, "AST 1300 detected\n"); + break; + default: + ast->chip = AST2300; + drm_info(dev, "AST 2300 detected\n"); + break; + } } else if (pdev->revision >= 0x10) { switch (scu_rev & 0x0300) { case 0x0200: @@ -179,15 +201,21 @@ static int ast_detect_chip(struct drm_device *dev, bool *need_post) drm_info(dev, "AST 2100 detected\n"); break; } - ast->vga2_clone = false; } else { ast->chip = AST2000; drm_info(dev, "AST 2000 detected\n"); } + return 0; +} + +static void ast_detect_widescreen(struct ast_device *ast) +{ + u8 jreg; + /* Check if we support wide screen */ - switch (ast->chip) { - case AST2000: + switch (AST_GEN(ast)) { + case 1: ast->support_wide_screen = false; break; default: @@ -198,20 +226,23 @@ static int ast_detect_chip(struct drm_device *dev, bool *need_post) ast->support_wide_screen = true; else { ast->support_wide_screen = false; - if (ast->chip == AST2300 && - (scu_rev & 0x300) == 0x0) /* ast1300 */ + if (ast->chip == AST1300) ast->support_wide_screen = true; - if (ast->chip == AST2400 && - (scu_rev & 0x300) == 0x100) /* ast1400 */ + if (ast->chip == AST1400) ast->support_wide_screen = true; - if (ast->chip == AST2500 && - scu_rev == 0x100) /* ast2510 */ + if (ast->chip == AST2510) ast->support_wide_screen = true; - if (ast->chip == AST2600) /* ast2600 */ + if (IS_AST_GEN7(ast)) ast->support_wide_screen = true; } break; } +} + +static void ast_detect_tx_chip(struct ast_device *ast, bool need_post) +{ + struct drm_device *dev = &ast->base; + u8 jreg; /* Check 3rd Tx option (digital output afaik) */ ast->tx_chip_types |= AST_TX_NONE_BIT; @@ -224,15 +255,15 @@ static int ast_detect_chip(struct drm_device *dev, bool *need_post) * is at power-on reset, otherwise we'll incorrectly "detect" a * SIL164 when there is none. */ - if (!*need_post) { + if (!need_post) { jreg = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xa3, 0xff); if (jreg & 0x80) ast->tx_chip_types = AST_TX_SIL164_BIT; } - if ((ast->chip == AST2300) || (ast->chip == AST2400) || (ast->chip == AST2500)) { + if (IS_AST_GEN4(ast) || IS_AST_GEN5(ast) || IS_AST_GEN6(ast)) { /* - * On AST2300 and 2400, look the configuration set by the SoC in + * On AST GEN4+, look the configuration set by the SoC in * the SOC scratch register #1 bits 11:8 (interestingly marked * as "reserved" in the spec) */ @@ -254,7 +285,7 @@ static int ast_detect_chip(struct drm_device *dev, bool *need_post) case 0x0c: ast->tx_chip_types = AST_TX_DP501_BIT; } - } else if (ast->chip == AST2600) { + } else if (IS_AST_GEN7(ast)) { if (ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xD1, TX_TYPE_MASK) == ASTDP_DPMCU_TX) { ast->tx_chip_types = AST_TX_ASTDP_BIT; @@ -271,8 +302,6 @@ static int ast_detect_chip(struct drm_device *dev, bool *need_post) drm_info(dev, "Using DP501 DisplayPort transmitter\n"); if (ast->tx_chip_types & AST_TX_ASTDP_BIT) drm_info(dev, "Using ASPEED DisplayPort transmitter\n"); - - return 0; } static int ast_get_dram_info(struct drm_device *dev) @@ -286,7 +315,7 @@ static int ast_get_dram_info(struct drm_device *dev) case ast_use_dt: /* * If some properties are missing, use reasonable - * defaults for AST2400 + * defaults for GEN5 */ if (of_property_read_u32(np, "aspeed,mcr-configuration", &mcr_cfg)) @@ -309,7 +338,7 @@ static int ast_get_dram_info(struct drm_device *dev) default: ast->dram_bus_width = 16; ast->dram_type = AST_DRAM_1Gx16; - if (ast->chip == AST2500) + if (IS_AST_GEN6(ast)) ast->mclk = 800; else ast->mclk = 396; @@ -321,7 +350,7 @@ static int ast_get_dram_info(struct drm_device *dev) else ast->dram_bus_width = 32; - if (ast->chip == AST2500) { + if (IS_AST_GEN6(ast)) { switch (mcr_cfg & 0x03) { case 0: ast->dram_type = AST_DRAM_1Gx16; @@ -337,7 +366,7 @@ static int ast_get_dram_info(struct drm_device *dev) ast->dram_type = AST_DRAM_8Gx16; break; } - } else if (ast->chip == AST2300 || ast->chip == AST2400) { + } else if (IS_AST_GEN4(ast) || IS_AST_GEN5(ast)) { switch (mcr_cfg & 0x03) { case 0: ast->dram_type = AST_DRAM_512Mx16; @@ -395,25 +424,13 @@ static int ast_get_dram_info(struct drm_device *dev) return 0; } -/* - * Run this function as part of the HW device cleanup; not - * when the DRM device gets released. - */ -static void ast_device_release(void *data) -{ - struct ast_device *ast = data; - - /* enable standard VGA decode */ - ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0xa1, 0x04); -} - struct ast_device *ast_device_create(const struct drm_driver *drv, struct pci_dev *pdev, unsigned long flags) { struct drm_device *dev; struct ast_device *ast; - bool need_post; + bool need_post = false; int ret = 0; ast = devm_drm_dev_alloc(&pdev->dev, drv, struct ast_device, base); @@ -449,7 +466,30 @@ struct ast_device *ast_device_create(const struct drm_driver *drv, return ERR_PTR(-EIO); } - ast_detect_chip(dev, &need_post); + if (!ast_is_vga_enabled(dev)) { + drm_info(dev, "VGA not enabled on entry, requesting chip POST\n"); + need_post = true; + } + + /* + * If VGA isn't enabled, we need to enable now or subsequent + * access to the scratch registers will fail. + */ + if (need_post) + ast_enable_vga(dev); + + /* Enable extended register access */ + ast_open_key(ast); + ret = ast_enable_mmio(ast); + if (ret) + return ERR_PTR(ret); + + ret = ast_device_config_init(ast); + if (ret) + return ERR_PTR(ret); + + ast_detect_widescreen(ast); + ast_detect_tx_chip(ast, need_post); ret = ast_get_dram_info(dev); if (ret) @@ -477,9 +517,5 @@ struct ast_device *ast_device_create(const struct drm_driver *drv, if (ret) return ERR_PTR(ret); - ret = devm_add_action_or_reset(dev->dev, ast_device_release, ast); - if (ret) - return ERR_PTR(ret); - return ast; } |