diff --git a/msm/Kbuild b/msm/Kbuild index 50b7f029c7..0235ddad30 100644 --- a/msm/Kbuild +++ b/msm/Kbuild @@ -172,6 +172,7 @@ msm_drm-$(CONFIG_DRM_MSM_SDE) += sde/sde_crtc.o \ sde/sde_hw_qdss.o \ sde_dsc_helper.o \ sde_vdc_helper.o \ + sde/sde_hw_dnsc_blur.o \ sde/sde_hw_rc.o msm_drm-$(CONFIG_DRM_SDE_WB) += sde/sde_wb.o \ diff --git a/msm/sde/sde_hw_catalog.h b/msm/sde/sde_hw_catalog.h index fe4aa3911d..9fb92dc843 100644 --- a/msm/sde/sde_hw_catalog.h +++ b/msm/sde/sde_hw_catalog.h @@ -486,6 +486,18 @@ enum { SDE_VDC_MAX }; +/** + * Downscale Blur sub-blocks/features + * @SDE_DNSC_BLUR_GAUS_LUT Downscale Blur Gaussian LUT sub block + * @SDE_DNSC_BLUR_DITHER Downscale Blur Dither sub block + * @SDE_DNSC_BLUR_MAX + */ +enum { + SDE_DNSC_BLUR_GAUS_LUT, + SDE_DNSC_BLUR_DITHER, + SDE_DNSC_BLUR_MAX +}; + /** * CTL sub-blocks * @SDE_CTL_SPLIT_DISPLAY CTL supports video mode split display @@ -788,6 +800,14 @@ struct sde_vdc_blk { SDE_HW_SUBBLK_INFO; }; +/** + * struct sde_dnsc_blur_blk : Downscale Blur sub-blk information + * @info: HW register and features supported by this sub-blk + */ +struct sde_dnsc_blur_blk { + SDE_HW_SUBBLK_INFO; +}; + /** * struct sde_format_extended - define sde specific pixel format+modifier * @fourcc_format: Base FOURCC pixel format code @@ -999,6 +1019,16 @@ struct sde_vdc_sub_blks { struct sde_vdc_blk ctl; }; +/** + * struct sde_dnsc_blur_sub_blks : Downscale Blur sub-blks + * @gaus_lut: Gaussian coef LUT register offset(relative to Downscale Blur base) + * @dither: Dither register offset(relative to Downscale Blur base) + */ +struct sde_dnsc_blur_sub_blks { + struct sde_dnsc_blur_blk gaus_lut; + struct sde_dnsc_blur_blk dither; +}; + struct sde_wb_sub_blocks { u32 maxlinewidth; u32 maxlinewidth_linear; @@ -1309,6 +1339,18 @@ struct sde_cdm_cfg { unsigned long wb_connect; }; +/** + * struct sde_dnsc_blur_cfg - information of Downscale Blur blocks + * @id enum identifying this block + * @base register offset of this block + * @features bit mask identifying sub-blocks/features + * @sblk sub-blocks associated with Downscale Blur + */ +struct sde_dnsc_blur_cfg { + SDE_HW_BLK_INFO; + struct sde_dnsc_blur_sub_blks *sblk; +}; + /** * struct sde_intf_cfg - information of timing engine blocks * @id enum identifying this block @@ -1740,6 +1782,8 @@ struct sde_mdss_cfg { struct sde_vdc_cfg vdc[MAX_BLOCKS]; u32 cdm_count; struct sde_cdm_cfg cdm[MAX_BLOCKS]; + u32 dnsc_blur_count; + struct sde_dnsc_blur_cfg dnsc_blur[MAX_BLOCKS]; u32 intf_count; struct sde_intf_cfg intf[MAX_BLOCKS]; u32 wb_count; diff --git a/msm/sde/sde_hw_dnsc_blur.c b/msm/sde/sde_hw_dnsc_blur.c new file mode 100644 index 0000000000..47248504e1 --- /dev/null +++ b/msm/sde/sde_hw_dnsc_blur.c @@ -0,0 +1,275 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2021, The Linux Foundation. All rights reserved. + */ + +#include "sde_hw_mdss.h" +#include "sde_hwio.h" +#include "sde_hw_catalog.h" +#include "sde_hw_dnsc_blur.h" +#include "sde_dbg.h" +#include "sde_kms.h" + +#define DNSC_BLUR_OP_MODE 0x0 +#define DNSC_BLUR_BLUR_RATIO_H 0x4 +#define DNSC_BLUR_BLUR_RATIO_V 0x8 +#define DNSC_BLUR_PCMN_PHASE_INIT_H 0xC +#define DNSC_BLUR_PCMN_PHASE_STEP_H 0x10 +#define DNSC_BLUR_PCMN_PHASE_INIT_V 0x14 +#define DNSC_BLUR_PCMN_PHASE_STEP_V 0x18 +#define DNSC_BLUR_OUT_IMG_SIZE 0x1C +#define DNSC_BLUR_GAUS_COEF_LUT_SEL 0x20 +#define DNSC_BLUR_MUX 0x24 +#define DNSC_BLUR_SRC_IMG_SIZE 0x28 + +#define DNSC_BLUR_GAUS_COEF_LUT_H0 0x0 +#define DNSC_BLUR_GAUS_COEF_LUT_V0 0x100 +#define DNSC_BLUR_GAUS_COEF_LUT_H1 0x200 +#define DNSC_BLUR_GAUS_COEF_LUT_V1 0x300 + +#define DNSC_BLUR_DITHER_OP_MODE 0x0 +#define DNSC_BLUR_DITHER_BITDEPTH 0x4 +#define DNSC_BLUR_DITHER_MATRIX_ROW0 0x8 + +/* DNSC_BLUR_OP_MODE bits */ +#define DNSC_BLUR_OPMODE_ENABLE BIT(0) +#define DNSC_BLUR_OPMODE_DWNS_H_EN BIT(1) +#define DNSC_BLUR_OPMODE_DWNS_V_EN BIT(2) +#define DNSC_BLUR_OPMODE_PCMN_H BIT(8) +#define DNSC_BLUR_OPMODE_PCMN_V BIT(12) +#define DNSC_BLUR_OPMODE_OUT_RND_8B_EN BIT(16) + +static struct sde_dnsc_blur_cfg *_dnsc_blur_offset(enum sde_dnsc_blur idx, + struct sde_mdss_cfg *m, void __iomem *addr, struct sde_hw_blk_reg_map *b) +{ + int i; + + for (i = 0; i < m->dnsc_blur_count; i++) { + if (idx == m->dnsc_blur[i].id) { + b->base_off = addr; + b->blk_off = m->dnsc_blur[i].base; + b->length = m->dnsc_blur[i].len; + b->hw_rev = m->hw_rev; + b->log_mask = SDE_DBG_MASK_DNSC_BLUR; + return &m->dnsc_blur[i]; + } + } + + return ERR_PTR(-EINVAL); +} + +static inline int _dnsc_blur_subblk_offset(struct sde_hw_dnsc_blur *hw_dnsc_blur, + int s_id, u32 *base) +{ + const struct sde_dnsc_blur_sub_blks *sblk; + + sblk = hw_dnsc_blur->caps->sblk; + + switch (s_id) { + case SDE_DNSC_BLUR_GAUS_LUT: + *base = sblk->gaus_lut.base; + break; + case SDE_DNSC_BLUR_DITHER: + *base = sblk->dither.base; + break; + default: + return -EINVAL; + } + + return 0; +} + +static void _sde_hw_dnsc_blur_gaus_lut_setup(struct sde_hw_dnsc_blur *hw_dnsc_blur, + struct sde_drm_dnsc_blur_cfg *cfg, u32 lut_sel) +{ + struct sde_hw_blk_reg_map *hw = &hw_dnsc_blur->hw; + u32 lut_off, base; + int i; + + if (_dnsc_blur_subblk_offset(hw_dnsc_blur, SDE_DNSC_BLUR_GAUS_LUT, &base)) + return; + + SDE_REG_WRITE(hw, DNSC_BLUR_GAUS_COEF_LUT_SEL, lut_sel); + + if (cfg->flags_h & DNSC_BLUR_GAUS_FILTER) { + lut_off = lut_sel ? DNSC_BLUR_GAUS_COEF_LUT_H1 : DNSC_BLUR_GAUS_COEF_LUT_H0; + for (i = 0; i < DNSC_BLUR_COEF_NUM; i++) + SDE_REG_WRITE(hw, lut_off + (i * 0x4) + base, cfg->coef_hori[i]); + } + + if (cfg->flags_v & DNSC_BLUR_GAUS_FILTER) { + lut_off = lut_sel ? DNSC_BLUR_GAUS_COEF_LUT_V1 : DNSC_BLUR_GAUS_COEF_LUT_V0; + for (i = 0; i < DNSC_BLUR_COEF_NUM; i++) + SDE_REG_WRITE(hw, lut_off + (i * 0x4) + base, cfg->coef_vert[i]); + } +} + +static void _sde_hw_dnsc_blur_filter_setup(struct sde_hw_dnsc_blur *hw_dnsc_blur, + struct sde_drm_dnsc_blur_cfg *cfg, u32 lut_sel) +{ + struct sde_hw_blk_reg_map *hw = &hw_dnsc_blur->hw; + u32 val; + + /* PCMN */ + if (cfg->flags_h & DNSC_BLUR_PCMN_FILTER) { + SDE_REG_WRITE(hw, DNSC_BLUR_PCMN_PHASE_INIT_H, cfg->phase_init_h); + SDE_REG_WRITE(hw, DNSC_BLUR_PCMN_PHASE_STEP_H, cfg->phase_step_h); + } + + if (cfg->flags_v & DNSC_BLUR_PCMN_FILTER) { + SDE_REG_WRITE(hw, DNSC_BLUR_PCMN_PHASE_INIT_V, cfg->phase_init_v); + SDE_REG_WRITE(hw, DNSC_BLUR_PCMN_PHASE_STEP_V, cfg->phase_step_v); + } + + /* Gaussian */ + if (cfg->flags_h & DNSC_BLUR_GAUS_FILTER) { + val = (cfg->norm_h << 16) | cfg->ratio_h; + SDE_REG_WRITE(hw, DNSC_BLUR_BLUR_RATIO_H, val); + } + + if (cfg->flags_v & DNSC_BLUR_GAUS_FILTER) { + val = (cfg->norm_v << 16) | cfg->ratio_v; + SDE_REG_WRITE(hw, DNSC_BLUR_BLUR_RATIO_V, val); + } + + if ((cfg->flags_v | cfg->flags_h) & DNSC_BLUR_GAUS_FILTER) + _sde_hw_dnsc_blur_gaus_lut_setup(hw_dnsc_blur, cfg, lut_sel); +} + +static void _sde_hw_dnsc_blur_setup(struct sde_hw_dnsc_blur *hw_dnsc_blur, + struct sde_drm_dnsc_blur_cfg *cfg, u32 lut_sel) +{ + struct sde_hw_blk_reg_map *hw = &hw_dnsc_blur->hw; + u32 opmode = 0; + + /* disable, when no scaling involved */ + if (!cfg || !(cfg->flags & DNSC_BLUR_EN)) { + SDE_REG_WRITE(hw, DNSC_BLUR_OP_MODE, 0x0); + return; + } + + opmode = DNSC_BLUR_OPMODE_ENABLE; + opmode |= (cfg->flags & DNSC_BLUR_RND_8B_EN) ? DNSC_BLUR_OPMODE_OUT_RND_8B_EN : 0; + + if (cfg->flags_h) { + opmode |= DNSC_BLUR_OPMODE_DWNS_H_EN; + opmode |= (cfg->flags_h & DNSC_BLUR_PCMN_FILTER) ? DNSC_BLUR_OPMODE_PCMN_H : 0; + } + + if (cfg->flags_v) { + opmode |= DNSC_BLUR_OPMODE_DWNS_V_EN; + opmode |= (cfg->flags_v & DNSC_BLUR_PCMN_FILTER) ? DNSC_BLUR_OPMODE_PCMN_V : 0; + } + + _sde_hw_dnsc_blur_filter_setup(hw_dnsc_blur, cfg, lut_sel); + + SDE_REG_WRITE(hw, DNSC_BLUR_OP_MODE, opmode); + SDE_REG_WRITE(hw, DNSC_BLUR_OUT_IMG_SIZE, (cfg->dst_height << 16) | cfg->dst_width); + SDE_REG_WRITE(hw, DNSC_BLUR_SRC_IMG_SIZE, (cfg->src_height << 16) | cfg->src_width); +} + +static void _sde_hw_dnsc_blur_dither_setup(struct sde_hw_dnsc_blur *hw_dnsc_blur, + struct sde_drm_dnsc_blur_cfg *cfg) +{ + struct sde_hw_blk_reg_map *hw = &hw_dnsc_blur->hw; + int i; + u32 base, data, offset; + + if (_dnsc_blur_subblk_offset(hw_dnsc_blur, SDE_DNSC_BLUR_DITHER, &base)) + return; + + /* disable case */ + if (!cfg || !(cfg->flags & DNSC_BLUR_DITHER_EN)) { + SDE_REG_WRITE(hw, DNSC_BLUR_DITHER_OP_MODE + base, 0x0); + return; + } + + data = (dither_depth_map[cfg->c0_bitdepth] & REG_MASK(2)) | + ((dither_depth_map[cfg->c1_bitdepth] & REG_MASK(2)) << 2) | + ((dither_depth_map[cfg->c2_bitdepth] & REG_MASK(2)) << 4) | + ((dither_depth_map[cfg->c3_bitdepth] & REG_MASK(2)) << 6) | + ((cfg->temporal_en) ? (1 << 8) : 0); + SDE_REG_WRITE(hw, DNSC_BLUR_DITHER_BITDEPTH + base, data); + + offset = DNSC_BLUR_DITHER_MATRIX_ROW0; + for (i = 0; i < DNSC_BLUR_DITHER_MATRIX_SZ - 3; i += 4) { + data = (cfg->dither_matrix[i] & REG_MASK(4)) | + ((cfg->dither_matrix[i + 1] & REG_MASK(4)) << 4) | + ((cfg->dither_matrix[i + 2] & REG_MASK(4)) << 8) | + ((cfg->dither_matrix[i + 3] & REG_MASK(4)) << 12); + SDE_REG_WRITE(hw, base + offset, data); + offset += 4; + } + + data = BIT(0); + data |= (cfg->dither_flags & DNSC_BLUR_DITHER_LUMA_MODE) ? BIT(4) : 0; + SDE_REG_WRITE(hw, DNSC_BLUR_DITHER_OP_MODE + base, data); +} + +static void _sde_hw_dnsc_blur_bind_pingpong_blk(struct sde_hw_dnsc_blur *hw_dnsc_blur, + bool enable, const enum sde_pingpong pp) +{ + struct sde_hw_blk_reg_map *hw = &hw_dnsc_blur->hw; + int mux_cfg; + + if (enable && (pp < PINGPONG_0 || pp >= PINGPONG_MAX)) + return; + + mux_cfg = enable ? (pp - PINGPONG_0) & 0x7 : 0xF; + SDE_REG_WRITE(hw, DNSC_BLUR_MUX, mux_cfg); +} + +static void _setup_dnsc_blur_ops(struct sde_hw_dnsc_blur_ops *ops, unsigned long features) +{ + ops->setup_dnsc_blur = _sde_hw_dnsc_blur_setup; + ops->setup_dither = _sde_hw_dnsc_blur_dither_setup; + ops->bind_pingpong_blk = _sde_hw_dnsc_blur_bind_pingpong_blk; +} + +struct sde_hw_blk_reg_map *sde_hw_dnsc_blur_init(enum sde_dnsc_blur idx, + void __iomem *addr, struct sde_mdss_cfg *m) +{ + struct sde_hw_dnsc_blur *c; + struct sde_dnsc_blur_cfg *cfg; + + if (!addr || !m) + return ERR_PTR(-EINVAL); + + c = kzalloc(sizeof(*c), GFP_KERNEL); + if (!c) + return ERR_PTR(-ENOMEM); + + cfg = _dnsc_blur_offset(idx, m, addr, &c->hw); + if (IS_ERR_OR_NULL(cfg)) { + kfree(c); + return ERR_PTR(-EINVAL); + } + + c->idx = idx; + c->caps = cfg; + _setup_dnsc_blur_ops(&c->ops, c->caps->features); + + sde_dbg_reg_register_dump_range(SDE_DBG_NAME, cfg->name, c->hw.blk_off, + c->hw.blk_off + c->hw.length, c->hw.xin_id); + + if (cfg->sblk->gaus_lut.base && cfg->sblk->gaus_lut.len) + sde_dbg_reg_register_dump_range(SDE_DBG_NAME, cfg->sblk->gaus_lut.name, + c->hw.blk_off + cfg->sblk->gaus_lut.base, + c->hw.blk_off + cfg->sblk->gaus_lut.base + + cfg->sblk->gaus_lut.len, c->hw.xin_id); + + if (cfg->sblk->dither.base && cfg->sblk->dither.len) + sde_dbg_reg_register_dump_range(SDE_DBG_NAME, cfg->sblk->dither.name, + c->hw.blk_off + cfg->sblk->dither.base, + c->hw.blk_off + cfg->sblk->dither.base + + cfg->sblk->dither.len, c->hw.xin_id); + + return &c->hw; +} + +void sde_hw_dnsc_blur_destroy(struct sde_hw_blk_reg_map *hw) +{ + if (hw) + kfree(to_sde_hw_dnsc_blur(hw)); +} + diff --git a/msm/sde/sde_hw_dnsc_blur.h b/msm/sde/sde_hw_dnsc_blur.h new file mode 100644 index 0000000000..4e700650ea --- /dev/null +++ b/msm/sde/sde_hw_dnsc_blur.h @@ -0,0 +1,89 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2021, The Linux Foundation. All rights reserved. + */ + +#ifndef _SDE_HW_DNSC_BLUR_H +#define _SDE_HW_DNSC_BLUR_H + +#include +#include + +#include "sde_hw_mdss.h" + +struct sde_hw_dnsc_blur; + +/** + * sde_hw_dnsc_blur_ops - interface to the DNSC_BLUR HW driver functions + * Caller must call the init function to the dnsc_blur hw context for dnsc_blur + * Assumption is these functions will be called after clocks are enabled + */ +struct sde_hw_dnsc_blur_ops { + /** + * setup_dnsc_blur - downscale blur block setup + * @hw_dnsc_blur: Pointer to dnsc_blur context + * @cfg : Pointer to dnsc_blur configs + * @lut_sel: LUT index for Gausian filter + */ + void (*setup_dnsc_blur)(struct sde_hw_dnsc_blur *hw_dnsc_blur, + struct sde_drm_dnsc_blur_cfg *cfg, u32 lut_sel); + + /** + * setup_dither - Dither programming + * @hw_dnsc_blur: Pointer to dnsc_blur context + * @cfg : Pointer to dnsc_blur configs + */ + void (*setup_dither)(struct sde_hw_dnsc_blur *hw_dnsc_blur, + struct sde_drm_dnsc_blur_cfg *cfg); + + /** + * bind_pingpong_blk - connection with pingpong block which feeds pixels + * to downscale blur block + * @hw_dnsc_blur: Pointer to dnsc_blur context + * @enable: Boolean to indicate enable/disable of the binding + * @pp: Pingpong block idx for binding + */ + void (*bind_pingpong_blk)(struct sde_hw_dnsc_blur *hw_dnsc_blur, + bool enable, const enum sde_pingpong pp); +}; + +/** + * struct sde_hw_dnsc_blur - downscale blur description + * @hw: Block hardware details + * @caps: Pointer to block capabilities + * @idx: Downscale Blur index + * @ops: Pointer to operations for this block + */ +struct sde_hw_dnsc_blur { + struct sde_hw_blk_reg_map hw; + const struct sde_dnsc_blur_cfg *caps; + enum sde_dnsc_blur idx; + struct sde_hw_dnsc_blur_ops ops; +}; + +/** + * to_sde_hw_dnsc_blur - convert base hw object to sde_hw_dnsc_blur to container + * @hw: Pointer to base hardware block register map object + * return: Pointer to hardware block container + */ +static inline struct sde_hw_dnsc_blur *to_sde_hw_dnsc_blur(struct sde_hw_blk_reg_map *hw) +{ + return container_of(hw, struct sde_hw_dnsc_blur, hw); +} + +/** + * sde_hw_dnsc_blur_init - initializes the dnsc_blur hw driver object + * @idx: dnsc_blur index for which driver object is required + * @addr: mapped register io address of MDP + * @m: pointer to mdss catalog data + */ +struct sde_hw_blk_reg_map *sde_hw_dnsc_blur_init(enum sde_dnsc_blur idx, + void __iomem *addr, struct sde_mdss_cfg *m); + +/** + * sde_hw_dnsc_blur_destroy - destroys dnsc_blur driver context + * @hw: Pointer to hardware block register map object + */ +void sde_hw_dnsc_blur_destroy(struct sde_hw_blk_reg_map *hw); + +#endif /*_SDE_HW_DNSC_BLUR_H */ diff --git a/msm/sde/sde_hw_mdss.h b/msm/sde/sde_hw_mdss.h index 30b77daece..171856b5f6 100644 --- a/msm/sde/sde_hw_mdss.h +++ b/msm/sde/sde_hw_mdss.h @@ -9,6 +9,9 @@ #include #include +#include +#include + #include "msm_drv.h" #define SDE_DBG_NAME "sde" @@ -121,6 +124,7 @@ enum sde_hw_blk_type { SDE_HW_BLK_VDC, SDE_HW_BLK_MERGE_3D, SDE_HW_BLK_QDSS, + SDE_HW_BLK_DNSC_BLUR, SDE_HW_BLK_MAX, }; @@ -251,6 +255,11 @@ enum sde_cdm { CDM_MAX }; +enum sde_dnsc_blur { + DNSC_BLUR_0 = 1, + DNSC_BLUR__MAX +}; + enum sde_pingpong { PINGPONG_0 = 1, PINGPONG_1, @@ -598,6 +607,7 @@ struct sde_mdss_color { #define SDE_DBG_MASK_SID (1 << 15) #define SDE_DBG_MASK_QDSS (1 << 16) #define SDE_DBG_MASK_VDC (1 << 17) +#define SDE_DBG_MASK_DNSC_BLUR (1 << 18) /** * struct sde_hw_cp_cfg: hardware dspp/lm feature payload. diff --git a/msm/sde/sde_rm.c b/msm/sde/sde_rm.c index 46834cd6a6..cd14de6d0a 100644 --- a/msm/sde/sde_rm.c +++ b/msm/sde/sde_rm.c @@ -20,6 +20,7 @@ #include "sde_crtc.h" #include "sde_hw_qdss.h" #include "sde_vbif.h" +#include "sde_hw_dnsc_blur.h" #define RESERVED_BY_OTHER(h, r) \ (((h)->rsvp && ((h)->rsvp->enc_id != (r)->enc_id)) ||\ @@ -120,6 +121,7 @@ char sde_hw_blk_str[SDE_HW_BLK_MAX][SDE_HW_BLK_NAME_LEN] = { "vdc", "merge_3d", "qdss", + "dnsc_blur" }; /** @@ -544,6 +546,9 @@ static void _sde_rm_hw_destroy(enum sde_hw_blk_type type, struct sde_hw_blk_reg_ case SDE_HW_BLK_QDSS: sde_hw_qdss_destroy(hw); break; + case SDE_HW_BLK_DNSC_BLUR: + sde_hw_dnsc_blur_destroy(hw); + break; case SDE_HW_BLK_SSPP: /* SSPPs are not managed by the resource manager */ case SDE_HW_BLK_TOP: @@ -641,6 +646,9 @@ static int _sde_rm_hw_blk_create( case SDE_HW_BLK_QDSS: hw = sde_hw_qdss_init(id, mmio, cat); break; + case SDE_HW_BLK_DNSC_BLUR: + hw = sde_hw_dnsc_blur_init(id, mmio, cat); + break; case SDE_HW_BLK_SSPP: /* SSPPs are not managed by the resource manager */ case SDE_HW_BLK_TOP: @@ -776,6 +784,15 @@ static int _sde_rm_hw_blk_create_new(struct sde_rm *rm, } } + for (i = 0; i < cat->dnsc_blur_count; i++) { + rc = _sde_rm_hw_blk_create(rm, cat, mmio, SDE_HW_BLK_DNSC_BLUR, + cat->dnsc_blur[i].id, &cat->dnsc_blur[i]); + if (rc) { + SDE_ERROR("failed: dnsc_blur hw not available\n"); + goto fail; + } + } + for (i = 0; i < cat->qdss_count; i++) { rc = _sde_rm_hw_blk_create(rm, cat, mmio, SDE_HW_BLK_QDSS, cat->qdss[i].id, &cat->qdss[i]);