Home | History | Annotate | Line # | Download | only in dce
      1 /*	$NetBSD: amdgpu_dce_abm.c,v 1.3 2021/12/19 11:23:26 riastradh Exp $	*/
      2 
      3 /*
      4  * Copyright 2012-16 Advanced Micro Devices, Inc.
      5  *
      6  * Permission is hereby granted, free of charge, to any person obtaining a
      7  * copy of this software and associated documentation files (the "Software"),
      8  * to deal in the Software without restriction, including without limitation
      9  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
     10  * and/or sell copies of the Software, and to permit persons to whom the
     11  * Software is furnished to do so, subject to the following conditions:
     12  *
     13  * The above copyright notice and this permission notice shall be included in
     14  * all copies or substantial portions of the Software.
     15  *
     16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
     19  * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
     20  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
     21  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
     22  * OTHER DEALINGS IN THE SOFTWARE.
     23  *
     24  * Authors: AMD
     25  *
     26  */
     27 
     28 #include <sys/cdefs.h>
     29 __KERNEL_RCSID(0, "$NetBSD: amdgpu_dce_abm.c,v 1.3 2021/12/19 11:23:26 riastradh Exp $");
     30 
     31 #include <linux/slab.h>
     32 
     33 #include "dce_abm.h"
     34 #include "dm_services.h"
     35 #include "reg_helper.h"
     36 #include "fixed31_32.h"
     37 #include "dc.h"
     38 
     39 #include "atom.h"
     40 
     41 
     42 #define TO_DCE_ABM(abm)\
     43 	container_of(abm, struct dce_abm, base)
     44 
     45 #define REG(reg) \
     46 	(abm_dce->regs->reg)
     47 
     48 #undef FN
     49 #define FN(reg_name, field_name) \
     50 	abm_dce->abm_shift->field_name, abm_dce->abm_mask->field_name
     51 
     52 #define DC_LOGGER \
     53 	abm->ctx->logger
     54 #define CTX \
     55 	abm_dce->base.ctx
     56 
     57 #define MCP_ABM_LEVEL_SET 0x65
     58 #define MCP_ABM_PIPE_SET 0x66
     59 #define MCP_BL_SET 0x67
     60 
     61 #define MCP_DISABLE_ABM_IMMEDIATELY 255
     62 
     63 static bool dce_abm_set_pipe(struct abm *abm, uint32_t controller_id)
     64 {
     65 	struct dce_abm *abm_dce = TO_DCE_ABM(abm);
     66 	uint32_t rampingBoundary = 0xFFFF;
     67 
     68 	if (abm->dmcu_is_running == false)
     69 		return true;
     70 
     71 	REG_WAIT(MASTER_COMM_CNTL_REG, MASTER_COMM_INTERRUPT, 0,
     72 			1, 80000);
     73 
     74 	/* set ramping boundary */
     75 	REG_WRITE(MASTER_COMM_DATA_REG1, rampingBoundary);
     76 
     77 	/* setDMCUParam_Pipe */
     78 	REG_UPDATE_2(MASTER_COMM_CMD_REG,
     79 			MASTER_COMM_CMD_REG_BYTE0, MCP_ABM_PIPE_SET,
     80 			MASTER_COMM_CMD_REG_BYTE1, controller_id);
     81 
     82 	/* notifyDMCUMsg */
     83 	REG_UPDATE(MASTER_COMM_CNTL_REG, MASTER_COMM_INTERRUPT, 1);
     84 
     85 	REG_WAIT(MASTER_COMM_CNTL_REG, MASTER_COMM_INTERRUPT, 0,
     86 			1, 80000);
     87 
     88 	return true;
     89 }
     90 
     91 static unsigned int calculate_16_bit_backlight_from_pwm(struct dce_abm *abm_dce)
     92 {
     93 	uint64_t current_backlight;
     94 	uint32_t round_result;
     95 	uint32_t pwm_period_cntl __unused, bl_period, bl_int_count;
     96 	uint32_t bl_pwm_cntl __unused, bl_pwm, fractional_duty_cycle_en;
     97 	uint32_t bl_period_mask, bl_pwm_mask;
     98 
     99 	pwm_period_cntl = REG_READ(BL_PWM_PERIOD_CNTL);
    100 	REG_GET(BL_PWM_PERIOD_CNTL, BL_PWM_PERIOD, &bl_period);
    101 	REG_GET(BL_PWM_PERIOD_CNTL, BL_PWM_PERIOD_BITCNT, &bl_int_count);
    102 
    103 	bl_pwm_cntl = REG_READ(BL_PWM_CNTL);
    104 	REG_GET(BL_PWM_CNTL, BL_ACTIVE_INT_FRAC_CNT, (uint32_t *)(&bl_pwm));
    105 	REG_GET(BL_PWM_CNTL, BL_PWM_FRACTIONAL_EN, &fractional_duty_cycle_en);
    106 
    107 	if (bl_int_count == 0)
    108 		bl_int_count = 16;
    109 
    110 	bl_period_mask = (1 << bl_int_count) - 1;
    111 	bl_period &= bl_period_mask;
    112 
    113 	bl_pwm_mask = bl_period_mask << (16 - bl_int_count);
    114 
    115 	if (fractional_duty_cycle_en == 0)
    116 		bl_pwm &= bl_pwm_mask;
    117 	else
    118 		bl_pwm &= 0xFFFF;
    119 
    120 	current_backlight = bl_pwm << (1 + bl_int_count);
    121 
    122 	if (bl_period == 0)
    123 		bl_period = 0xFFFF;
    124 
    125 	current_backlight = div_u64(current_backlight, bl_period);
    126 	current_backlight = (current_backlight + 1) >> 1;
    127 
    128 	current_backlight = (uint64_t)(current_backlight) * bl_period;
    129 
    130 	round_result = (uint32_t)(current_backlight & 0xFFFFFFFF);
    131 
    132 	round_result = (round_result >> (bl_int_count-1)) & 1;
    133 
    134 	current_backlight >>= bl_int_count;
    135 	current_backlight += round_result;
    136 
    137 	return (uint32_t)(current_backlight);
    138 }
    139 
    140 static void driver_set_backlight_level(struct dce_abm *abm_dce,
    141 		uint32_t backlight_pwm_u16_16)
    142 {
    143 	uint32_t backlight_16bit;
    144 	uint32_t masked_pwm_period;
    145 	uint8_t bit_count;
    146 	uint64_t active_duty_cycle;
    147 	uint32_t pwm_period_bitcnt;
    148 
    149 	/*
    150 	 * 1. Find  16 bit backlight active duty cycle, where 0 <= backlight
    151 	 * active duty cycle <= backlight period
    152 	 */
    153 
    154 	/* 1.1 Apply bitmask for backlight period value based on value of BITCNT
    155 	 */
    156 	REG_GET_2(BL_PWM_PERIOD_CNTL,
    157 			BL_PWM_PERIOD_BITCNT, &pwm_period_bitcnt,
    158 			BL_PWM_PERIOD, &masked_pwm_period);
    159 
    160 	if (pwm_period_bitcnt == 0)
    161 		bit_count = 16;
    162 	else
    163 		bit_count = pwm_period_bitcnt;
    164 
    165 	/* e.g. maskedPwmPeriod = 0x24 when bitCount is 6 */
    166 	masked_pwm_period = masked_pwm_period & ((1 << bit_count) - 1);
    167 
    168 	/* 1.2 Calculate integer active duty cycle required upper 16 bits
    169 	 * contain integer component, lower 16 bits contain fractional component
    170 	 * of active duty cycle e.g. 0x21BDC0 = 0xEFF0 * 0x24
    171 	 */
    172 	active_duty_cycle = backlight_pwm_u16_16 * masked_pwm_period;
    173 
    174 	/* 1.3 Calculate 16 bit active duty cycle from integer and fractional
    175 	 * components shift by bitCount then mask 16 bits and add rounding bit
    176 	 * from MSB of fraction e.g. 0x86F7 = ((0x21BDC0 >> 6) & 0xFFF) + 0
    177 	 */
    178 	backlight_16bit = active_duty_cycle >> bit_count;
    179 	backlight_16bit &= 0xFFFF;
    180 	backlight_16bit += (active_duty_cycle >> (bit_count - 1)) & 0x1;
    181 
    182 	/*
    183 	 * 2. Program register with updated value
    184 	 */
    185 
    186 	/* 2.1 Lock group 2 backlight registers */
    187 
    188 	REG_UPDATE_2(BL_PWM_GRP1_REG_LOCK,
    189 			BL_PWM_GRP1_IGNORE_MASTER_LOCK_EN, 1,
    190 			BL_PWM_GRP1_REG_LOCK, 1);
    191 
    192 	// 2.2 Write new active duty cycle
    193 	REG_UPDATE(BL_PWM_CNTL, BL_ACTIVE_INT_FRAC_CNT, backlight_16bit);
    194 
    195 	/* 2.3 Unlock group 2 backlight registers */
    196 	REG_UPDATE(BL_PWM_GRP1_REG_LOCK,
    197 			BL_PWM_GRP1_REG_LOCK, 0);
    198 
    199 	/* 3 Wait for pending bit to be cleared */
    200 	REG_WAIT(BL_PWM_GRP1_REG_LOCK,
    201 			BL_PWM_GRP1_REG_UPDATE_PENDING, 0,
    202 			1, 10000);
    203 }
    204 
    205 static void dmcu_set_backlight_level(
    206 	struct dce_abm *abm_dce,
    207 	uint32_t backlight_pwm_u16_16,
    208 	uint32_t frame_ramp,
    209 	uint32_t controller_id)
    210 {
    211 	unsigned int backlight_8_bit = 0;
    212 	uint32_t s2;
    213 
    214 	if (backlight_pwm_u16_16 & 0x10000)
    215 		// Check for max backlight condition
    216 		backlight_8_bit = 0xFF;
    217 	else
    218 		// Take MSB of fractional part since backlight is not max
    219 		backlight_8_bit = (backlight_pwm_u16_16 >> 8) & 0xFF;
    220 
    221 	dce_abm_set_pipe(&abm_dce->base, controller_id);
    222 
    223 	/* waitDMCUReadyForCmd */
    224 	REG_WAIT(MASTER_COMM_CNTL_REG, MASTER_COMM_INTERRUPT,
    225 			0, 1, 80000);
    226 
    227 	/* setDMCUParam_BL */
    228 	REG_UPDATE(BL1_PWM_USER_LEVEL, BL1_PWM_USER_LEVEL, backlight_pwm_u16_16);
    229 
    230 	/* write ramp */
    231 	if (controller_id == 0)
    232 		frame_ramp = 0;
    233 	REG_WRITE(MASTER_COMM_DATA_REG1, frame_ramp);
    234 
    235 	/* setDMCUParam_Cmd */
    236 	REG_UPDATE(MASTER_COMM_CMD_REG, MASTER_COMM_CMD_REG_BYTE0, MCP_BL_SET);
    237 
    238 	/* notifyDMCUMsg */
    239 	REG_UPDATE(MASTER_COMM_CNTL_REG, MASTER_COMM_INTERRUPT, 1);
    240 
    241 	/* UpdateRequestedBacklightLevel */
    242 	s2 = REG_READ(BIOS_SCRATCH_2);
    243 
    244 	s2 &= ~ATOM_S2_CURRENT_BL_LEVEL_MASK;
    245 	backlight_8_bit &= (ATOM_S2_CURRENT_BL_LEVEL_MASK >>
    246 				ATOM_S2_CURRENT_BL_LEVEL_SHIFT);
    247 	s2 |= (backlight_8_bit << ATOM_S2_CURRENT_BL_LEVEL_SHIFT);
    248 
    249 	REG_WRITE(BIOS_SCRATCH_2, s2);
    250 
    251 	/* waitDMCUReadyForCmd */
    252 	REG_WAIT(MASTER_COMM_CNTL_REG, MASTER_COMM_INTERRUPT,
    253 			0, 1, 80000);
    254 }
    255 
    256 static void dce_abm_init(struct abm *abm)
    257 {
    258 	struct dce_abm *abm_dce = TO_DCE_ABM(abm);
    259 	unsigned int backlight = calculate_16_bit_backlight_from_pwm(abm_dce);
    260 
    261 	REG_WRITE(DC_ABM1_HG_SAMPLE_RATE, 0x103);
    262 	REG_WRITE(DC_ABM1_HG_SAMPLE_RATE, 0x101);
    263 	REG_WRITE(DC_ABM1_LS_SAMPLE_RATE, 0x103);
    264 	REG_WRITE(DC_ABM1_LS_SAMPLE_RATE, 0x101);
    265 	REG_WRITE(BL1_PWM_BL_UPDATE_SAMPLE_RATE, 0x101);
    266 
    267 	REG_SET_3(DC_ABM1_HG_MISC_CTRL, 0,
    268 			ABM1_HG_NUM_OF_BINS_SEL, 0,
    269 			ABM1_HG_VMAX_SEL, 1,
    270 			ABM1_HG_BIN_BITWIDTH_SIZE_SEL, 0);
    271 
    272 	REG_SET_3(DC_ABM1_IPCSC_COEFF_SEL, 0,
    273 			ABM1_IPCSC_COEFF_SEL_R, 2,
    274 			ABM1_IPCSC_COEFF_SEL_G, 4,
    275 			ABM1_IPCSC_COEFF_SEL_B, 2);
    276 
    277 	REG_UPDATE(BL1_PWM_CURRENT_ABM_LEVEL,
    278 			BL1_PWM_CURRENT_ABM_LEVEL, backlight);
    279 
    280 	REG_UPDATE(BL1_PWM_TARGET_ABM_LEVEL,
    281 			BL1_PWM_TARGET_ABM_LEVEL, backlight);
    282 
    283 	REG_UPDATE(BL1_PWM_USER_LEVEL,
    284 			BL1_PWM_USER_LEVEL, backlight);
    285 
    286 	REG_UPDATE_2(DC_ABM1_LS_MIN_MAX_PIXEL_VALUE_THRES,
    287 			ABM1_LS_MIN_PIXEL_VALUE_THRES, 0,
    288 			ABM1_LS_MAX_PIXEL_VALUE_THRES, 1000);
    289 
    290 	REG_SET_3(DC_ABM1_HGLS_REG_READ_PROGRESS, 0,
    291 			ABM1_HG_REG_READ_MISSED_FRAME_CLEAR, 1,
    292 			ABM1_LS_REG_READ_MISSED_FRAME_CLEAR, 1,
    293 			ABM1_BL_REG_READ_MISSED_FRAME_CLEAR, 1);
    294 }
    295 
    296 static unsigned int dce_abm_get_current_backlight(struct abm *abm)
    297 {
    298 	struct dce_abm *abm_dce = TO_DCE_ABM(abm);
    299 	unsigned int backlight = REG_READ(BL1_PWM_CURRENT_ABM_LEVEL);
    300 
    301 	/* return backlight in hardware format which is unsigned 17 bits, with
    302 	 * 1 bit integer and 16 bit fractional
    303 	 */
    304 	return backlight;
    305 }
    306 
    307 static unsigned int dce_abm_get_target_backlight(struct abm *abm)
    308 {
    309 	struct dce_abm *abm_dce = TO_DCE_ABM(abm);
    310 	unsigned int backlight = REG_READ(BL1_PWM_TARGET_ABM_LEVEL);
    311 
    312 	/* return backlight in hardware format which is unsigned 17 bits, with
    313 	 * 1 bit integer and 16 bit fractional
    314 	 */
    315 	return backlight;
    316 }
    317 
    318 static bool dce_abm_set_level(struct abm *abm, uint32_t level)
    319 {
    320 	struct dce_abm *abm_dce = TO_DCE_ABM(abm);
    321 
    322 	if (abm->dmcu_is_running == false)
    323 		return true;
    324 
    325 	REG_WAIT(MASTER_COMM_CNTL_REG, MASTER_COMM_INTERRUPT, 0,
    326 			1, 80000);
    327 
    328 	/* setDMCUParam_ABMLevel */
    329 	REG_UPDATE_2(MASTER_COMM_CMD_REG,
    330 			MASTER_COMM_CMD_REG_BYTE0, MCP_ABM_LEVEL_SET,
    331 			MASTER_COMM_CMD_REG_BYTE2, level);
    332 
    333 	/* notifyDMCUMsg */
    334 	REG_UPDATE(MASTER_COMM_CNTL_REG, MASTER_COMM_INTERRUPT, 1);
    335 
    336 	return true;
    337 }
    338 
    339 static bool dce_abm_immediate_disable(struct abm *abm)
    340 {
    341 	struct dce_abm *abm_dce = TO_DCE_ABM(abm);
    342 
    343 	if (abm->dmcu_is_running == false)
    344 		return true;
    345 
    346 	dce_abm_set_pipe(abm, MCP_DISABLE_ABM_IMMEDIATELY);
    347 
    348 	abm->stored_backlight_registers.BL_PWM_CNTL =
    349 		REG_READ(BL_PWM_CNTL);
    350 	abm->stored_backlight_registers.BL_PWM_CNTL2 =
    351 		REG_READ(BL_PWM_CNTL2);
    352 	abm->stored_backlight_registers.BL_PWM_PERIOD_CNTL =
    353 		REG_READ(BL_PWM_PERIOD_CNTL);
    354 
    355 	REG_GET(LVTMA_PWRSEQ_REF_DIV, BL_PWM_REF_DIV,
    356 		&abm->stored_backlight_registers.LVTMA_PWRSEQ_REF_DIV_BL_PWM_REF_DIV);
    357 	return true;
    358 }
    359 
    360 static bool dce_abm_init_backlight(struct abm *abm)
    361 {
    362 	struct dce_abm *abm_dce = TO_DCE_ABM(abm);
    363 	uint32_t value;
    364 
    365 	/* It must not be 0, so we have to restore them
    366 	 * Bios bug w/a - period resets to zero,
    367 	 * restoring to cache values which is always correct
    368 	 */
    369 	REG_GET(BL_PWM_CNTL, BL_ACTIVE_INT_FRAC_CNT, &value);
    370 	if (value == 0 || value == 1) {
    371 		if (abm->stored_backlight_registers.BL_PWM_CNTL != 0) {
    372 			REG_WRITE(BL_PWM_CNTL,
    373 				abm->stored_backlight_registers.BL_PWM_CNTL);
    374 			REG_WRITE(BL_PWM_CNTL2,
    375 				abm->stored_backlight_registers.BL_PWM_CNTL2);
    376 			REG_WRITE(BL_PWM_PERIOD_CNTL,
    377 				abm->stored_backlight_registers.BL_PWM_PERIOD_CNTL);
    378 			REG_UPDATE(LVTMA_PWRSEQ_REF_DIV,
    379 				BL_PWM_REF_DIV,
    380 				abm->stored_backlight_registers.
    381 				LVTMA_PWRSEQ_REF_DIV_BL_PWM_REF_DIV);
    382 		} else {
    383 			/* TODO: Note: This should not really happen since VBIOS
    384 			 * should have initialized PWM registers on boot.
    385 			 */
    386 			REG_WRITE(BL_PWM_CNTL, 0xC000FA00);
    387 			REG_WRITE(BL_PWM_PERIOD_CNTL, 0x000C0FA0);
    388 		}
    389 	} else {
    390 		abm->stored_backlight_registers.BL_PWM_CNTL =
    391 				REG_READ(BL_PWM_CNTL);
    392 		abm->stored_backlight_registers.BL_PWM_CNTL2 =
    393 				REG_READ(BL_PWM_CNTL2);
    394 		abm->stored_backlight_registers.BL_PWM_PERIOD_CNTL =
    395 				REG_READ(BL_PWM_PERIOD_CNTL);
    396 
    397 		REG_GET(LVTMA_PWRSEQ_REF_DIV, BL_PWM_REF_DIV,
    398 				&abm->stored_backlight_registers.
    399 				LVTMA_PWRSEQ_REF_DIV_BL_PWM_REF_DIV);
    400 	}
    401 
    402 	/* Have driver take backlight control
    403 	 * TakeBacklightControl(true)
    404 	 */
    405 	value = REG_READ(BIOS_SCRATCH_2);
    406 	value |= ATOM_S2_VRI_BRIGHT_ENABLE;
    407 	REG_WRITE(BIOS_SCRATCH_2, value);
    408 
    409 	/* Enable the backlight output */
    410 	REG_UPDATE(BL_PWM_CNTL, BL_PWM_EN, 1);
    411 
    412 	/* Disable fractional pwm if configured */
    413 	REG_UPDATE(BL_PWM_CNTL, BL_PWM_FRACTIONAL_EN,
    414 		   abm->ctx->dc->config.disable_fractional_pwm ? 0 : 1);
    415 
    416 	/* Unlock group 2 backlight registers */
    417 	REG_UPDATE(BL_PWM_GRP1_REG_LOCK,
    418 			BL_PWM_GRP1_REG_LOCK, 0);
    419 
    420 	return true;
    421 }
    422 
    423 static bool dce_abm_set_backlight_level_pwm(
    424 		struct abm *abm,
    425 		unsigned int backlight_pwm_u16_16,
    426 		unsigned int frame_ramp,
    427 		unsigned int controller_id,
    428 		bool use_smooth_brightness)
    429 {
    430 	struct dce_abm *abm_dce = TO_DCE_ABM(abm);
    431 
    432 	DC_LOG_BACKLIGHT("New Backlight level: %d (0x%X)\n",
    433 			backlight_pwm_u16_16, backlight_pwm_u16_16);
    434 
    435 	/* If DMCU is in reset state, DMCU is uninitialized */
    436 	if (use_smooth_brightness)
    437 		dmcu_set_backlight_level(abm_dce,
    438 				backlight_pwm_u16_16,
    439 				frame_ramp,
    440 				controller_id);
    441 	else
    442 		driver_set_backlight_level(abm_dce, backlight_pwm_u16_16);
    443 
    444 	return true;
    445 }
    446 
    447 static const struct abm_funcs dce_funcs = {
    448 	.abm_init = dce_abm_init,
    449 	.set_abm_level = dce_abm_set_level,
    450 	.init_backlight = dce_abm_init_backlight,
    451 	.set_pipe = dce_abm_set_pipe,
    452 	.set_backlight_level_pwm = dce_abm_set_backlight_level_pwm,
    453 	.get_current_backlight = dce_abm_get_current_backlight,
    454 	.get_target_backlight = dce_abm_get_target_backlight,
    455 	.set_abm_immediate_disable = dce_abm_immediate_disable
    456 };
    457 
    458 static void dce_abm_construct(
    459 	struct dce_abm *abm_dce,
    460 	struct dc_context *ctx,
    461 	const struct dce_abm_registers *regs,
    462 	const struct dce_abm_shift *abm_shift,
    463 	const struct dce_abm_mask *abm_mask)
    464 {
    465 	struct abm *base = &abm_dce->base;
    466 
    467 	base->ctx = ctx;
    468 	base->funcs = &dce_funcs;
    469 	base->stored_backlight_registers.BL_PWM_CNTL = 0;
    470 	base->stored_backlight_registers.BL_PWM_CNTL2 = 0;
    471 	base->stored_backlight_registers.BL_PWM_PERIOD_CNTL = 0;
    472 	base->stored_backlight_registers.LVTMA_PWRSEQ_REF_DIV_BL_PWM_REF_DIV = 0;
    473 	base->dmcu_is_running = false;
    474 
    475 	abm_dce->regs = regs;
    476 	abm_dce->abm_shift = abm_shift;
    477 	abm_dce->abm_mask = abm_mask;
    478 }
    479 
    480 struct abm *dce_abm_create(
    481 	struct dc_context *ctx,
    482 	const struct dce_abm_registers *regs,
    483 	const struct dce_abm_shift *abm_shift,
    484 	const struct dce_abm_mask *abm_mask)
    485 {
    486 	struct dce_abm *abm_dce = kzalloc(sizeof(*abm_dce), GFP_KERNEL);
    487 
    488 	if (abm_dce == NULL) {
    489 		BREAK_TO_DEBUGGER();
    490 		return NULL;
    491 	}
    492 
    493 	dce_abm_construct(abm_dce, ctx, regs, abm_shift, abm_mask);
    494 
    495 	abm_dce->base.funcs = &dce_funcs;
    496 
    497 	return &abm_dce->base;
    498 }
    499 
    500 void dce_abm_destroy(struct abm **abm)
    501 {
    502 	struct dce_abm *abm_dce = TO_DCE_ABM(*abm);
    503 
    504 	kfree(abm_dce);
    505 	*abm = NULL;
    506 }
    507