static int gk20a_scale_qos_notify(struct notifier_block *nb, unsigned long n, void *p) { struct gk20a_scale_profile *profile = container_of(nb, struct gk20a_scale_profile, qos_notify_block); struct gk20a_platform *platform = platform_get_drvdata(profile->pdev); struct gk20a *g = get_gk20a(profile->pdev); unsigned long freq; if (!platform->postscale) return NOTIFY_OK; /* get the frequency requirement. if devfreq is enabled, check if it * has higher demand than qos */ freq = gk20a_clk_round_rate(g, pm_qos_request(platform->qos_id)); if (g->devfreq) freq = max(g->devfreq->previous_freq, freq); /* Update gpu load because we may scale the emc target * if the gpu load changed. */ gk20a_pmu_load_update(g); platform->postscale(profile->pdev, freq); return NOTIFY_OK; }
void gk20a_create_sysfs(struct platform_device *dev) { struct gk20a *g = get_gk20a(dev); int error = 0; error |= device_create_file(&dev->dev, &dev_attr_elcg_enable); error |= device_create_file(&dev->dev, &dev_attr_blcg_enable); error |= device_create_file(&dev->dev, &dev_attr_slcg_enable); error |= device_create_file(&dev->dev, &dev_attr_ptimer_scale_factor); error |= device_create_file(&dev->dev, &dev_attr_elpg_enable); error |= device_create_file(&dev->dev, &dev_attr_emc3d_ratio); error |= device_create_file(&dev->dev, &dev_attr_counters); error |= device_create_file(&dev->dev, &dev_attr_counters_reset); error |= device_create_file(&dev->dev, &dev_attr_load); error |= device_create_file(&dev->dev, &dev_attr_railgate_delay); error |= device_create_file(&dev->dev, &dev_attr_clockgate_delay); #ifdef CONFIG_PM_RUNTIME error |= device_create_file(&dev->dev, &dev_attr_force_idle); #endif error |= device_create_file(&dev->dev, &dev_attr_aelpg_param); error |= device_create_file(&dev->dev, &dev_attr_aelpg_enable); error |= device_create_file(&dev->dev, &dev_attr_allow_all); error |= device_create_file(&dev->dev, &dev_attr_tpc_fs_mask); if (g->host1x_dev && (dev->dev.parent != &g->host1x_dev->dev)) error |= sysfs_create_link(&g->host1x_dev->dev.kobj, &dev->dev.kobj, dev_name(&dev->dev)); if (error) dev_err(&dev->dev, "Failed to create sysfs attributes!\n"); }
static ssize_t elpg_enable_store(struct device *device, struct device_attribute *attr, const char *buf, size_t count) { struct platform_device *ndev = to_platform_device(device); struct gk20a *g = get_gk20a(ndev); unsigned long val = 0; int err; if (kstrtoul(buf, 10, &val) < 0) return -EINVAL; /* * Since elpg is refcounted, we should not unnecessarily call * enable/disable if it is already so. */ err = gk20a_busy(g->dev); if (err) return -EAGAIN; if (val && !g->elpg_enabled) { g->elpg_enabled = true; gk20a_pmu_enable_elpg(g); } else if (!val && g->elpg_enabled) { g->elpg_enabled = false; gk20a_pmu_disable_elpg(g); } gk20a_idle(g->dev); dev_info(device, "ELPG is %s.\n", g->elpg_enabled ? "enabled" : "disabled"); return count; }
static ssize_t gk20a_load_show(struct device *dev, struct device_attribute *attr, char *buf) { struct platform_device *pdev = to_platform_device(dev); struct gk20a *g = get_gk20a(pdev); u32 busy_time; ssize_t res; int err; if (!g->power_on) { busy_time = 0; } else { err = gk20a_busy(g->dev); if (err) return err; gk20a_pmu_load_update(g); gk20a_pmu_load_norm(g, &busy_time); gk20a_idle(g->dev); } res = snprintf(buf, PAGE_SIZE, "%u\n", busy_time); return res; }
static ssize_t allow_all_enable_read(struct device *device, struct device_attribute *attr, char *buf) { struct platform_device *ndev = to_platform_device(device); struct gk20a *g = get_gk20a(ndev); return sprintf(buf, "%d\n", g->allow_all ? 1 : 0); }
int clk_gk20a_debugfs_init(struct platform_device *dev) { struct dentry *d; struct nvhost_device_data *pdata = platform_get_drvdata(dev); struct gk20a *g = get_gk20a(dev); d = debugfs_create_file( "rate", S_IRUGO|S_IWUSR, pdata->debugfs, g, &rate_fops); if (!d) goto err_out; d = debugfs_create_file( "pll_reg", S_IRUGO, pdata->debugfs, g, &pll_reg_fops); if (!d) goto err_out; d = debugfs_create_file( "monitor", S_IRUGO, pdata->debugfs, g, &monitor_fops); if (!d) goto err_out; return 0; err_out: pr_err("%s: Failed to make debugfs node\n", __func__); debugfs_remove_recursive(pdata->debugfs); return -ENOMEM; }
void gk20a_remove_sysfs(struct device *dev) { struct gk20a *g = get_gk20a(to_platform_device(dev)); device_remove_file(dev, &dev_attr_elcg_enable); device_remove_file(dev, &dev_attr_blcg_enable); device_remove_file(dev, &dev_attr_slcg_enable); device_remove_file(dev, &dev_attr_ptimer_scale_factor); device_remove_file(dev, &dev_attr_elpg_enable); device_remove_file(dev, &dev_attr_emc3d_ratio); device_remove_file(dev, &dev_attr_counters); device_remove_file(dev, &dev_attr_counters_reset); device_remove_file(dev, &dev_attr_load); device_remove_file(dev, &dev_attr_railgate_delay); device_remove_file(dev, &dev_attr_clockgate_delay); #ifdef CONFIG_PM_RUNTIME device_remove_file(dev, &dev_attr_force_idle); #endif device_remove_file(dev, &dev_attr_aelpg_param); device_remove_file(dev, &dev_attr_aelpg_enable); device_remove_file(dev, &dev_attr_allow_all); device_remove_file(dev, &dev_attr_tpc_fs_mask); if (g->host1x_dev && (dev->parent != &g->host1x_dev->dev)) sysfs_remove_link(&g->host1x_dev->dev.kobj, dev_name(dev)); }
static ssize_t elcg_enable_store(struct device *device, struct device_attribute *attr, const char *buf, size_t count) { struct platform_device *ndev = to_platform_device(device); struct gk20a *g = get_gk20a(ndev); unsigned long val = 0; int err; if (kstrtoul(buf, 10, &val) < 0) return -EINVAL; err = gk20a_busy(g->dev); if (err) return err; if (val) { g->elcg_enabled = true; gr_gk20a_init_elcg_mode(g, ELCG_AUTO, ENGINE_GR_GK20A); gr_gk20a_init_elcg_mode(g, ELCG_AUTO, ENGINE_CE2_GK20A); } else { g->elcg_enabled = false; gr_gk20a_init_elcg_mode(g, ELCG_RUN, ENGINE_GR_GK20A); gr_gk20a_init_elcg_mode(g, ELCG_RUN, ENGINE_CE2_GK20A); } gk20a_idle(g->dev); dev_info(device, "ELCG is %s.\n", g->elcg_enabled ? "enabled" : "disabled"); return count; }
static ssize_t tpc_fs_mask_read(struct device *device, struct device_attribute *attr, char *buf) { struct platform_device *ndev = to_platform_device(device); struct gk20a *g = get_gk20a(ndev); struct gr_gk20a *gr = &g->gr; u32 gpc_index; u32 tpc_fs_mask = 0; int err = 0; err = gk20a_busy(g->dev); if (err) return err; for (gpc_index = 0; gpc_index < gr->gpc_count; gpc_index++) { if (g->ops.gr.get_gpc_tpc_mask) tpc_fs_mask |= g->ops.gr.get_gpc_tpc_mask(g, gpc_index) << (gr->max_tpc_per_gpc_count * gpc_index); } gk20a_idle(g->dev); return sprintf(buf, "0x%x\n", tpc_fs_mask); }
static ssize_t emc3d_ratio_read(struct device *device, struct device_attribute *attr, char *buf) { struct platform_device *ndev = to_platform_device(device); struct gk20a *g = get_gk20a(ndev); return sprintf(buf, "%d\n", g->emc3d_ratio); }
void gk20a_scale_resume(struct platform_device *pdev) { struct gk20a *g = get_gk20a(pdev); struct devfreq *devfreq = g->devfreq; if (!devfreq) return; devfreq_resume_device(devfreq); }
static ssize_t aelpg_param_read(struct device *device, struct device_attribute *attr, char *buf) { struct platform_device *ndev = to_platform_device(device); struct gk20a *g = get_gk20a(ndev); return sprintf(buf, "%d %d %d %d %d\n", g->pmu.aelpg_param[0], g->pmu.aelpg_param[1], g->pmu.aelpg_param[2], g->pmu.aelpg_param[3], g->pmu.aelpg_param[4]); }
static ssize_t slcg_enable_store(struct device *device, struct device_attribute *attr, const char *buf, size_t count) { struct platform_device *ndev = to_platform_device(device); struct gk20a *g = get_gk20a(ndev); unsigned long val = 0; int err; if (kstrtoul(buf, 10, &val) < 0) return -EINVAL; if (val) g->slcg_enabled = true; else g->slcg_enabled = false; /* * TODO: slcg_therm_load_gating is not enabled anywhere during * init. Therefore, it would be incongruous to add it here. Once * it is added to init, we should add it here too. */ err = gk20a_busy(g->dev); if (err) return err; if (g->ops.clock_gating.slcg_bus_load_gating_prod) g->ops.clock_gating.slcg_bus_load_gating_prod(g, g->slcg_enabled); if (g->ops.clock_gating.slcg_ce2_load_gating_prod) g->ops.clock_gating.slcg_ce2_load_gating_prod(g, g->slcg_enabled); if (g->ops.clock_gating.slcg_chiplet_load_gating_prod) g->ops.clock_gating.slcg_chiplet_load_gating_prod(g, g->slcg_enabled); if (g->ops.clock_gating.slcg_ctxsw_firmware_load_gating_prod) g->ops.clock_gating.slcg_ctxsw_firmware_load_gating_prod(g, g->slcg_enabled); if (g->ops.clock_gating.slcg_fb_load_gating_prod) g->ops.clock_gating.slcg_fb_load_gating_prod(g, g->slcg_enabled); if (g->ops.clock_gating.slcg_fifo_load_gating_prod) g->ops.clock_gating.slcg_fifo_load_gating_prod(g, g->slcg_enabled); g->ops.clock_gating.slcg_gr_load_gating_prod(g, g->slcg_enabled); if (g->ops.clock_gating.slcg_ltc_load_gating_prod) g->ops.clock_gating.slcg_ltc_load_gating_prod(g, g->slcg_enabled); g->ops.clock_gating.slcg_perf_load_gating_prod(g, g->slcg_enabled); if (g->ops.clock_gating.slcg_priring_load_gating_prod) g->ops.clock_gating.slcg_priring_load_gating_prod(g, g->slcg_enabled); if (g->ops.clock_gating.slcg_pmu_load_gating_prod) g->ops.clock_gating.slcg_pmu_load_gating_prod(g, g->slcg_enabled); if (g->ops.clock_gating.slcg_xbar_load_gating_prod) g->ops.clock_gating.slcg_xbar_load_gating_prod(g, g->slcg_enabled); gk20a_idle(g->dev); dev_info(device, "SLCG is %s.\n", g->slcg_enabled ? "enabled" : "disabled"); return count; }
static ssize_t counters_show_reset(struct device *dev, struct device_attribute *attr, char *buf) { ssize_t res = counters_show(dev, attr, buf); struct platform_device *pdev = to_platform_device(dev); struct gk20a *g = get_gk20a(pdev); gk20a_pmu_reset_load_counters(g); return res; }
static ssize_t counters_show(struct device *dev, struct device_attribute *attr, char *buf) { struct platform_device *pdev = to_platform_device(dev); struct gk20a *g = get_gk20a(pdev); u32 busy_cycles, total_cycles; ssize_t res; gk20a_pmu_get_load_counters(g, &busy_cycles, &total_cycles); res = snprintf(buf, PAGE_SIZE, "%u %u\n", busy_cycles, total_cycles); return res; }
static ssize_t emc3d_ratio_store(struct device *device, struct device_attribute *attr, const char *buf, size_t count) { struct platform_device *ndev = to_platform_device(device); struct gk20a *g = get_gk20a(ndev); unsigned long val = 0; if (kstrtoul(buf, 10, &val) < 0) return -EINVAL; g->emc3d_ratio = val; return count; }
static void update_load_estimate_gpmu(struct platform_device *pdev) { struct gk20a *g = get_gk20a(pdev); struct gk20a_scale_profile *profile = g->scale_profile; unsigned long dt; u32 busy_time; ktime_t t; t = ktime_get(); dt = ktime_us_delta(t, profile->last_event_time); profile->dev_stat.total_time = dt; profile->last_event_time = t; gk20a_pmu_load_norm(g, &busy_time); profile->dev_stat.busy_time = (busy_time * dt) / 1000; }
static ssize_t allow_all_enable_store(struct device *device, struct device_attribute *attr, const char *buf, size_t count) { struct platform_device *ndev = to_platform_device(device); struct gk20a *g = get_gk20a(ndev); unsigned long val = 0; int err; if (kstrtoul(buf, 10, &val) < 0) return -EINVAL; err = gk20a_busy(g->dev); g->allow_all = (val ? true : false); gk20a_idle(g->dev); return count; }
static ssize_t aelpg_param_store(struct device *device, struct device_attribute *attr, const char *buf, size_t count) { struct platform_device *ndev = to_platform_device(device); struct gk20a *g = get_gk20a(ndev); int status = 0; union pmu_ap_cmd ap_cmd; int *paramlist = (int *)g->pmu.aelpg_param; u32 defaultparam[5] = { APCTRL_SAMPLING_PERIOD_PG_DEFAULT_US, APCTRL_MINIMUM_IDLE_FILTER_DEFAULT_US, APCTRL_MINIMUM_TARGET_SAVING_DEFAULT_US, APCTRL_POWER_BREAKEVEN_DEFAULT_US, APCTRL_CYCLES_PER_SAMPLE_MAX_DEFAULT }; /* Get each parameter value from input string*/ sscanf(buf, "%d %d %d %d %d", ¶mlist[0], ¶mlist[1], ¶mlist[2], ¶mlist[3], ¶mlist[4]); /* If parameter value is 0 then reset to SW default values*/ if ((paramlist[0] | paramlist[1] | paramlist[2] | paramlist[3] | paramlist[4]) == 0x00) { memcpy(paramlist, defaultparam, sizeof(defaultparam)); } /* If aelpg is enabled & pmu is ready then post values to * PMU else store then post later */ if (g->aelpg_enabled && g->pmu.pmu_ready) { /* Disable AELPG */ ap_cmd.init.cmd_id = PMU_AP_CMD_ID_DISABLE_CTRL; status = gk20a_pmu_ap_send_command(g, &ap_cmd, false); /* Enable AELPG */ gk20a_aelpg_init(g); gk20a_aelpg_init_and_enable(g, PMU_AP_CTRL_ID_GRAPHICS); } return count; }
static int gk20a_scale_make_freq_table(struct gk20a_scale_profile *profile) { struct gk20a *g = get_gk20a(profile->pdev); unsigned long *freqs; int num_freqs, err; /* make sure the clock is available */ if (!gk20a_clk_get(g)) return -ENOSYS; /* get gpu dvfs table */ err = tegra_dvfs_get_freqs(clk_get_parent(g->clk.tegra_clk), &freqs, &num_freqs); if (err) return -ENOSYS; profile->devfreq_profile.freq_table = (unsigned long *)freqs; profile->devfreq_profile.max_state = num_freqs; return 0; }
static int gk20a_scale_get_dev_status(struct device *dev, struct devfreq_dev_status *stat) { struct gk20a *g = get_gk20a(to_platform_device(dev)); struct gk20a_scale_profile *profile = g->scale_profile; /* Make sure there are correct values for the current frequency */ profile->dev_stat.current_frequency = gk20a_clk_get_rate(g); /* Update load estimate */ update_load_estimate_gpmu(to_platform_device(dev)); /* Copy the contents of the current device status */ *stat = profile->dev_stat; /* Finally, clear out the local values */ profile->dev_stat.total_time = 0; profile->dev_stat.busy_time = 0; return 0; }
static ssize_t blcg_enable_store(struct device *device, struct device_attribute *attr, const char *buf, size_t count) { struct platform_device *ndev = to_platform_device(device); struct gk20a *g = get_gk20a(ndev); unsigned long val = 0; int err; if (kstrtoul(buf, 10, &val) < 0) return -EINVAL; if (val) g->blcg_enabled = true; else g->blcg_enabled = false; err = gk20a_busy(g->dev); if (err) return err; if (g->ops.clock_gating.blcg_bus_load_gating_prod) g->ops.clock_gating.blcg_bus_load_gating_prod(g, g->blcg_enabled); if (g->ops.clock_gating.blcg_ctxsw_firmware_load_gating_prod) g->ops.clock_gating.blcg_ctxsw_firmware_load_gating_prod(g, g->blcg_enabled); if (g->ops.clock_gating.blcg_fb_load_gating_prod) g->ops.clock_gating.blcg_fb_load_gating_prod(g, g->blcg_enabled); if (g->ops.clock_gating.blcg_fifo_load_gating_prod) g->ops.clock_gating.blcg_fifo_load_gating_prod(g, g->blcg_enabled); g->ops.clock_gating.blcg_gr_load_gating_prod(g, g->blcg_enabled); if (g->ops.clock_gating.blcg_ltc_load_gating_prod) g->ops.clock_gating.blcg_ltc_load_gating_prod(g, g->blcg_enabled); if (g->ops.clock_gating.blcg_pmu_load_gating_prod) g->ops.clock_gating.blcg_pmu_load_gating_prod(g, g->blcg_enabled); gk20a_idle(g->dev); dev_info(device, "BLCG is %s.\n", g->blcg_enabled ? "enabled" : "disabled"); return count; }
static ssize_t aelpg_enable_store(struct device *device, struct device_attribute *attr, const char *buf, size_t count) { struct platform_device *ndev = to_platform_device(device); struct gk20a *g = get_gk20a(ndev); unsigned long val = 0; int status = 0; union pmu_ap_cmd ap_cmd; int err; if (kstrtoul(buf, 10, &val) < 0) return -EINVAL; err = gk20a_busy(g->dev); if (err) return err; if (g->pmu.pmu_ready) { if (val && !g->aelpg_enabled) { g->aelpg_enabled = true; /* Enable AELPG */ ap_cmd.init.cmd_id = PMU_AP_CMD_ID_ENABLE_CTRL; status = gk20a_pmu_ap_send_command(g, &ap_cmd, false); } else if (!val && g->aelpg_enabled) { g->aelpg_enabled = false; /* Disable AELPG */ ap_cmd.init.cmd_id = PMU_AP_CMD_ID_DISABLE_CTRL; status = gk20a_pmu_ap_send_command(g, &ap_cmd, false); } } else { dev_info(device, "PMU is not ready, AELPG request failed\n"); } gk20a_idle(g->dev); dev_info(device, "AELPG is %s.\n", g->aelpg_enabled ? "enabled" : "disabled"); return count; }
static void gk20a_scale_notify(struct platform_device *pdev, bool busy) { struct gk20a_platform *platform = platform_get_drvdata(pdev); struct gk20a *g = get_gk20a(pdev); struct gk20a_scale_profile *profile = g->scale_profile; struct devfreq *devfreq = g->devfreq; /* update the software shadow */ gk20a_pmu_load_update(g); /* inform edp about new constraint */ if (platform->prescale) platform->prescale(pdev); /* Is the device profile initialised? */ if (!(profile && devfreq)) return; mutex_lock(&devfreq->lock); profile->dev_stat.busy = busy; update_devfreq(devfreq); mutex_unlock(&devfreq->lock); }
static int gk20a_scale_target(struct device *dev, unsigned long *freq, u32 flags) { struct gk20a *g = get_gk20a(to_platform_device(dev)); struct gk20a_platform *platform = dev_get_drvdata(dev); struct gk20a_scale_profile *profile = g->scale_profile; unsigned long rounded_rate = gk20a_clk_round_rate(g, *freq); if (gk20a_clk_get_rate(g) == rounded_rate) *freq = rounded_rate; else { gk20a_clk_set_rate(g, rounded_rate); *freq = gk20a_clk_get_rate(g); } /* postscale will only scale emc (dram clock) if evaluating * gk20a_tegra_get_emc_rate() produces a new or different emc * target because the load or_and gpufreq has changed */ if (platform->postscale) platform->postscale(profile->pdev, rounded_rate); return 0; }
static ssize_t force_idle_store(struct device *device, struct device_attribute *attr, const char *buf, size_t count) { struct platform_device *ndev = to_platform_device(device); struct gk20a *g = get_gk20a(ndev); unsigned long val = 0; int err = 0; if (kstrtoul(buf, 10, &val) < 0) return -EINVAL; if (val) { if (g->forced_idle) return count; /* do nothing */ else { err = __gk20a_do_idle(ndev); if (!err) { g->forced_idle = 1; dev_info(device, "gpu is idle : %d\n", g->forced_idle); } } } else { if (!g->forced_idle) return count; /* do nothing */ else { err = __gk20a_do_unidle(ndev); if (!err) { g->forced_idle = 0; dev_info(device, "gpu is idle : %d\n", g->forced_idle); } } } return count; }
static ssize_t tpc_fs_mask_store(struct device *device, struct device_attribute *attr, const char *buf, size_t count) { struct platform_device *ndev = to_platform_device(device); struct gk20a *g = get_gk20a(ndev); unsigned long val = 0; if (kstrtoul(buf, 10, &val) < 0) return -EINVAL; if (val && val != g->gr.gpc_tpc_mask[0] && g->ops.gr.set_gpc_tpc_mask) { g->gr.gpc_tpc_mask[0] = val; g->ops.gr.set_gpc_tpc_mask(g, 0); kfree(g->gr.ctx_vars.local_golden_image); g->gr.ctx_vars.local_golden_image = NULL; g->gr.ctx_vars.golden_image_initialized = false; g->gr.ctx_vars.golden_image_size = 0; g->gr.sw_ready = false; } return count; }
long gk20a_ctrl_dev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { struct platform_device *dev = filp->private_data; struct gk20a *g = get_gk20a(dev); struct nvhost_gpu_zcull_get_ctx_size_args *get_ctx_size_args; struct nvhost_gpu_zcull_get_info_args *get_info_args; struct nvhost_gpu_zbc_set_table_args *set_table_args; struct nvhost_gpu_zbc_query_table_args *query_table_args; u8 buf[NVHOST_GPU_IOCTL_MAX_ARG_SIZE]; struct gr_zcull_info *zcull_info; struct zbc_entry *zbc_val; struct zbc_query_params *zbc_tbl; int i, err = 0; nvhost_dbg_fn(""); if ((_IOC_TYPE(cmd) != NVHOST_GPU_IOCTL_MAGIC) || (_IOC_NR(cmd) == 0) || (_IOC_NR(cmd) > NVHOST_GPU_IOCTL_LAST)) return -EFAULT; BUG_ON(_IOC_SIZE(cmd) > NVHOST_GPU_IOCTL_MAX_ARG_SIZE); if (_IOC_DIR(cmd) & _IOC_WRITE) { if (copy_from_user(buf, (void __user *)arg, _IOC_SIZE(cmd))) return -EFAULT; } if (!g->gr.sw_ready) { gk20a_busy(g->dev); gk20a_idle(g->dev); } switch (cmd) { case NVHOST_GPU_IOCTL_ZCULL_GET_CTX_SIZE: get_ctx_size_args = (struct nvhost_gpu_zcull_get_ctx_size_args *)buf; get_ctx_size_args->size = gr_gk20a_get_ctxsw_zcull_size(g, &g->gr); break; case NVHOST_GPU_IOCTL_ZCULL_GET_INFO: get_info_args = (struct nvhost_gpu_zcull_get_info_args *)buf; memset(get_info_args, 0, sizeof(struct nvhost_gpu_zcull_get_info_args)); zcull_info = kzalloc(sizeof(struct gr_zcull_info), GFP_KERNEL); if (zcull_info == NULL) return -ENOMEM; err = gr_gk20a_get_zcull_info(g, &g->gr, zcull_info); if (err) break; get_info_args->width_align_pixels = zcull_info->width_align_pixels; get_info_args->height_align_pixels = zcull_info->height_align_pixels; get_info_args->pixel_squares_by_aliquots = zcull_info->pixel_squares_by_aliquots; get_info_args->aliquot_total = zcull_info->aliquot_total; get_info_args->region_byte_multiplier = zcull_info->region_byte_multiplier; get_info_args->region_header_size = zcull_info->region_header_size; get_info_args->subregion_header_size = zcull_info->subregion_header_size; get_info_args->subregion_width_align_pixels = zcull_info->subregion_width_align_pixels; get_info_args->subregion_height_align_pixels = zcull_info->subregion_height_align_pixels; get_info_args->subregion_count = zcull_info->subregion_count; if (zcull_info) kfree(zcull_info); break; case NVHOST_GPU_IOCTL_ZBC_SET_TABLE: set_table_args = (struct nvhost_gpu_zbc_set_table_args *)buf; zbc_val = kzalloc(sizeof(struct zbc_entry), GFP_KERNEL); if (zbc_val == NULL) return -ENOMEM; zbc_val->format = set_table_args->format; zbc_val->type = set_table_args->type; switch (zbc_val->type) { case GK20A_ZBC_TYPE_COLOR: for (i = 0; i < GK20A_ZBC_COLOR_VALUE_SIZE; i++) { zbc_val->color_ds[i] = set_table_args->color_ds[i]; zbc_val->color_l2[i] = set_table_args->color_l2[i]; } break; case GK20A_ZBC_TYPE_DEPTH: zbc_val->depth = set_table_args->depth; break; default: err = -EINVAL; } if (!err) { gk20a_busy(dev); err = gk20a_gr_zbc_set_table(g, &g->gr, zbc_val); gk20a_idle(dev); } if (zbc_val) kfree(zbc_val); break; case NVHOST_GPU_IOCTL_ZBC_QUERY_TABLE: query_table_args = (struct nvhost_gpu_zbc_query_table_args *)buf; zbc_tbl = kzalloc(sizeof(struct zbc_query_params), GFP_KERNEL); if (zbc_tbl == NULL) return -ENOMEM; zbc_tbl->type = query_table_args->type; zbc_tbl->index_size = query_table_args->index_size; err = gr_gk20a_query_zbc(g, &g->gr, zbc_tbl); if (!err) { switch (zbc_tbl->type) { case GK20A_ZBC_TYPE_COLOR: for (i = 0; i < GK20A_ZBC_COLOR_VALUE_SIZE; i++) { query_table_args->color_ds[i] = zbc_tbl->color_ds[i]; query_table_args->color_l2[i] = zbc_tbl->color_l2[i]; } break; case GK20A_ZBC_TYPE_DEPTH: query_table_args->depth = zbc_tbl->depth; break; case GK20A_ZBC_TYPE_INVALID: query_table_args->index_size = zbc_tbl->index_size; break; default: err = -EINVAL; } if (!err) { query_table_args->format = zbc_tbl->format; query_table_args->ref_cnt = zbc_tbl->ref_cnt; } } if (zbc_tbl) kfree(zbc_tbl); break; case NVHOST_GPU_IOCTL_GET_CHARACTERISTICS: err = gk20a_ctrl_ioctl_gpu_characteristics( g, (struct nvhost_gpu_get_characteristics *)buf); break; default: nvhost_err(dev_from_gk20a(g), "unrecognized gpu ioctl cmd: 0x%x", cmd); err = -ENOTTY; break; } if ((err == 0) && (_IOC_DIR(cmd) & _IOC_READ)) err = copy_to_user((void __user *)arg, buf, _IOC_SIZE(cmd)); return err; }