linux/drivers/staging/media/atomisp/pci/atomisp_acc.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Support for Clovertrail PNW Camera Imaging ISP subsystem.
   4 *
   5 * Copyright (c) 2012 Intel Corporation. All Rights Reserved.
   6 *
   7 * This program is free software; you can redistribute it and/or
   8 * modify it under the terms of the GNU General Public License version
   9 * 2 as published by the Free Software Foundation.
  10 *
  11 * This program is distributed in the hope that it will be useful,
  12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14 * GNU General Public License for more details.
  15 *
  16 *
  17 */
  18
  19/*
  20 * This file implements loadable acceleration firmware API,
  21 * including ioctls to map and unmap acceleration parameters and buffers.
  22 */
  23
  24#include <linux/init.h>
  25#include <media/v4l2-event.h>
  26
  27#include "hmm.h"
  28
  29#include "atomisp_acc.h"
  30#include "atomisp_internal.h"
  31#include "atomisp_compat.h"
  32#include "atomisp_cmd.h"
  33
  34#include "ia_css.h"
  35
  36static const struct {
  37        unsigned int flag;
  38        enum ia_css_pipe_id pipe_id;
  39} acc_flag_to_pipe[] = {
  40        { ATOMISP_ACC_FW_LOAD_FL_PREVIEW, IA_CSS_PIPE_ID_PREVIEW },
  41        { ATOMISP_ACC_FW_LOAD_FL_COPY, IA_CSS_PIPE_ID_COPY },
  42        { ATOMISP_ACC_FW_LOAD_FL_VIDEO, IA_CSS_PIPE_ID_VIDEO },
  43        { ATOMISP_ACC_FW_LOAD_FL_CAPTURE, IA_CSS_PIPE_ID_CAPTURE },
  44        { ATOMISP_ACC_FW_LOAD_FL_ACC, IA_CSS_PIPE_ID_ACC }
  45};
  46
  47/*
  48 * Allocate struct atomisp_acc_fw along with space for firmware.
  49 * The returned struct atomisp_acc_fw is cleared (firmware region is not).
  50 */
  51static struct atomisp_acc_fw *acc_alloc_fw(unsigned int fw_size)
  52{
  53        struct atomisp_acc_fw *acc_fw;
  54
  55        acc_fw = kzalloc(sizeof(*acc_fw), GFP_KERNEL);
  56        if (!acc_fw)
  57                return NULL;
  58
  59        acc_fw->fw = vmalloc(fw_size);
  60        if (!acc_fw->fw) {
  61                kfree(acc_fw);
  62                return NULL;
  63        }
  64
  65        return acc_fw;
  66}
  67
  68static void acc_free_fw(struct atomisp_acc_fw *acc_fw)
  69{
  70        vfree(acc_fw->fw);
  71        kfree(acc_fw);
  72}
  73
  74static struct atomisp_acc_fw *
  75acc_get_fw(struct atomisp_sub_device *asd, unsigned int handle)
  76{
  77        struct atomisp_acc_fw *acc_fw;
  78
  79        list_for_each_entry(acc_fw, &asd->acc.fw, list)
  80                if (acc_fw->handle == handle)
  81                        return acc_fw;
  82
  83        return NULL;
  84}
  85
  86static struct atomisp_map *acc_get_map(struct atomisp_sub_device *asd,
  87                                       unsigned long css_ptr, size_t length)
  88{
  89        struct atomisp_map *atomisp_map;
  90
  91        list_for_each_entry(atomisp_map, &asd->acc.memory_maps, list) {
  92                if (atomisp_map->ptr == css_ptr &&
  93                    atomisp_map->length == length)
  94                        return atomisp_map;
  95        }
  96        return NULL;
  97}
  98
  99static int acc_stop_acceleration(struct atomisp_sub_device *asd)
 100{
 101        int ret;
 102
 103        ret = atomisp_css_stop_acc_pipe(asd);
 104        atomisp_css_destroy_acc_pipe(asd);
 105
 106        return ret;
 107}
 108
 109void atomisp_acc_cleanup(struct atomisp_device *isp)
 110{
 111        int i;
 112
 113        for (i = 0; i < isp->num_of_streams; i++)
 114                ida_destroy(&isp->asd[i].acc.ida);
 115}
 116
 117void atomisp_acc_release(struct atomisp_sub_device *asd)
 118{
 119        struct atomisp_acc_fw *acc_fw, *ta;
 120        struct atomisp_map *atomisp_map, *tm;
 121
 122        /* Stop acceleration if already running */
 123        if (asd->acc.pipeline)
 124                acc_stop_acceleration(asd);
 125
 126        /* Unload all loaded acceleration binaries */
 127        list_for_each_entry_safe(acc_fw, ta, &asd->acc.fw, list) {
 128                list_del(&acc_fw->list);
 129                ida_free(&asd->acc.ida, acc_fw->handle);
 130                acc_free_fw(acc_fw);
 131        }
 132
 133        /* Free all mapped memory blocks */
 134        list_for_each_entry_safe(atomisp_map, tm, &asd->acc.memory_maps, list) {
 135                list_del(&atomisp_map->list);
 136                hmm_free(atomisp_map->ptr);
 137                kfree(atomisp_map);
 138        }
 139}
 140
 141int atomisp_acc_load_to_pipe(struct atomisp_sub_device *asd,
 142                             struct atomisp_acc_fw_load_to_pipe *user_fw)
 143{
 144        static const unsigned int pipeline_flags =
 145            ATOMISP_ACC_FW_LOAD_FL_PREVIEW | ATOMISP_ACC_FW_LOAD_FL_COPY |
 146            ATOMISP_ACC_FW_LOAD_FL_VIDEO |
 147            ATOMISP_ACC_FW_LOAD_FL_CAPTURE | ATOMISP_ACC_FW_LOAD_FL_ACC;
 148
 149        struct atomisp_acc_fw *acc_fw;
 150        int handle;
 151
 152        if (!user_fw->data || user_fw->size < sizeof(*acc_fw->fw))
 153                return -EINVAL;
 154
 155        /* Binary has to be enabled at least for one pipeline */
 156        if (!(user_fw->flags & pipeline_flags))
 157                return -EINVAL;
 158
 159        /* We do not support other flags yet */
 160        if (user_fw->flags & ~pipeline_flags)
 161                return -EINVAL;
 162
 163        if (user_fw->type < ATOMISP_ACC_FW_LOAD_TYPE_OUTPUT ||
 164            user_fw->type > ATOMISP_ACC_FW_LOAD_TYPE_STANDALONE)
 165                return -EINVAL;
 166
 167        if (asd->acc.pipeline || asd->acc.extension_mode)
 168                return -EBUSY;
 169
 170        acc_fw = acc_alloc_fw(user_fw->size);
 171        if (!acc_fw)
 172                return -ENOMEM;
 173
 174        if (copy_from_user(acc_fw->fw, user_fw->data, user_fw->size)) {
 175                acc_free_fw(acc_fw);
 176                return -EFAULT;
 177        }
 178
 179        handle = ida_alloc(&asd->acc.ida, GFP_KERNEL);
 180        if (handle < 0) {
 181                acc_free_fw(acc_fw);
 182                return -ENOSPC;
 183        }
 184
 185        user_fw->fw_handle = handle;
 186        acc_fw->handle = handle;
 187        acc_fw->flags = user_fw->flags;
 188        acc_fw->type = user_fw->type;
 189        acc_fw->fw->handle = handle;
 190
 191        /*
 192         * correct isp firmware type in order ISP firmware can be appended
 193         * to correct pipe properly
 194         */
 195        if (acc_fw->fw->type == ia_css_isp_firmware) {
 196                static const int type_to_css[] = {
 197                        [ATOMISP_ACC_FW_LOAD_TYPE_OUTPUT] =
 198                        IA_CSS_ACC_OUTPUT,
 199                        [ATOMISP_ACC_FW_LOAD_TYPE_VIEWFINDER] =
 200                        IA_CSS_ACC_VIEWFINDER,
 201                        [ATOMISP_ACC_FW_LOAD_TYPE_STANDALONE] =
 202                        IA_CSS_ACC_STANDALONE,
 203                };
 204                acc_fw->fw->info.isp.type = type_to_css[acc_fw->type];
 205        }
 206
 207        list_add_tail(&acc_fw->list, &asd->acc.fw);
 208        return 0;
 209}
 210
 211int atomisp_acc_load(struct atomisp_sub_device *asd,
 212                     struct atomisp_acc_fw_load *user_fw)
 213{
 214        struct atomisp_acc_fw_load_to_pipe ltp = {0};
 215        int r;
 216
 217        ltp.flags = ATOMISP_ACC_FW_LOAD_FL_ACC;
 218        ltp.type = ATOMISP_ACC_FW_LOAD_TYPE_STANDALONE;
 219        ltp.size = user_fw->size;
 220        ltp.data = user_fw->data;
 221        r = atomisp_acc_load_to_pipe(asd, &ltp);
 222        user_fw->fw_handle = ltp.fw_handle;
 223        return r;
 224}
 225
 226int atomisp_acc_unload(struct atomisp_sub_device *asd, unsigned int *handle)
 227{
 228        struct atomisp_acc_fw *acc_fw;
 229
 230        if (asd->acc.pipeline || asd->acc.extension_mode)
 231                return -EBUSY;
 232
 233        acc_fw = acc_get_fw(asd, *handle);
 234        if (!acc_fw)
 235                return -EINVAL;
 236
 237        list_del(&acc_fw->list);
 238        ida_free(&asd->acc.ida, acc_fw->handle);
 239        acc_free_fw(acc_fw);
 240
 241        return 0;
 242}
 243
 244int atomisp_acc_start(struct atomisp_sub_device *asd, unsigned int *handle)
 245{
 246        struct atomisp_device *isp = asd->isp;
 247        struct atomisp_acc_fw *acc_fw;
 248        int ret;
 249        unsigned int nbin;
 250
 251        if (asd->acc.pipeline || asd->acc.extension_mode)
 252                return -EBUSY;
 253
 254        /* Invalidate caches. FIXME: should flush only necessary buffers */
 255        wbinvd();
 256
 257        ret = atomisp_css_create_acc_pipe(asd);
 258        if (ret)
 259                return ret;
 260
 261        nbin = 0;
 262        list_for_each_entry(acc_fw, &asd->acc.fw, list) {
 263                if (*handle != 0 && *handle != acc_fw->handle)
 264                        continue;
 265
 266                if (acc_fw->type != ATOMISP_ACC_FW_LOAD_TYPE_STANDALONE)
 267                        continue;
 268
 269                /* Add the binary into the pipeline */
 270                ret = atomisp_css_load_acc_binary(asd, acc_fw->fw, nbin);
 271                if (ret < 0) {
 272                        dev_err(isp->dev, "acc_load_binary failed\n");
 273                        goto err_stage;
 274                }
 275
 276                ret = atomisp_css_set_acc_parameters(acc_fw);
 277                if (ret < 0) {
 278                        dev_err(isp->dev, "acc_set_parameters failed\n");
 279                        goto err_stage;
 280                }
 281                nbin++;
 282        }
 283        if (nbin < 1) {
 284                /* Refuse creating pipelines with no binaries */
 285                dev_err(isp->dev, "%s: no acc binary available\n", __func__);
 286                ret = -EINVAL;
 287                goto err_stage;
 288        }
 289
 290        ret = atomisp_css_start_acc_pipe(asd);
 291        if (ret) {
 292                dev_err(isp->dev, "%s: atomisp_acc_start_acc_pipe failed\n",
 293                        __func__);
 294                goto err_stage;
 295        }
 296
 297        return 0;
 298
 299err_stage:
 300        atomisp_css_destroy_acc_pipe(asd);
 301        return ret;
 302}
 303
 304int atomisp_acc_wait(struct atomisp_sub_device *asd, unsigned int *handle)
 305{
 306        struct atomisp_device *isp = asd->isp;
 307        int ret;
 308
 309        if (!asd->acc.pipeline)
 310                return -ENOENT;
 311
 312        if (*handle && !acc_get_fw(asd, *handle))
 313                return -EINVAL;
 314
 315        ret = atomisp_css_wait_acc_finish(asd);
 316        if (acc_stop_acceleration(asd) == -EIO) {
 317                atomisp_reset(isp);
 318                return -EINVAL;
 319        }
 320
 321        return ret;
 322}
 323
 324void atomisp_acc_done(struct atomisp_sub_device *asd, unsigned int handle)
 325{
 326        struct v4l2_event event = { 0 };
 327
 328        event.type = V4L2_EVENT_ATOMISP_ACC_COMPLETE;
 329        event.u.frame_sync.frame_sequence = atomic_read(&asd->sequence);
 330        event.id = handle;
 331
 332        v4l2_event_queue(asd->subdev.devnode, &event);
 333}
 334
 335int atomisp_acc_map(struct atomisp_sub_device *asd, struct atomisp_acc_map *map)
 336{
 337        struct atomisp_map *atomisp_map;
 338        ia_css_ptr cssptr;
 339        int pgnr;
 340
 341        if (map->css_ptr)
 342                return -EINVAL;
 343
 344        if (asd->acc.pipeline)
 345                return -EBUSY;
 346
 347        if (map->user_ptr) {
 348                /* Buffer to map must be page-aligned */
 349                if ((unsigned long)map->user_ptr & ~PAGE_MASK) {
 350                        dev_err(asd->isp->dev,
 351                                "%s: mapped buffer address %p is not page aligned\n",
 352                                __func__, map->user_ptr);
 353                        return -EINVAL;
 354                }
 355
 356                pgnr = DIV_ROUND_UP(map->length, PAGE_SIZE);
 357                if (pgnr < ((PAGE_ALIGN(map->length)) >> PAGE_SHIFT)) {
 358                        dev_err(asd->isp->dev,
 359                                "user space memory size is less than the expected size..\n");
 360                        return -ENOMEM;
 361                } else if (pgnr > ((PAGE_ALIGN(map->length)) >> PAGE_SHIFT)) {
 362                        dev_err(asd->isp->dev,
 363                                "user space memory size is large than the expected size..\n");
 364                        return -ENOMEM;
 365                }
 366
 367                cssptr = hmm_alloc(map->length, HMM_BO_USER, 0, map->user_ptr,
 368                                   map->flags & ATOMISP_MAP_FLAG_CACHED);
 369
 370        } else {
 371                /* Allocate private buffer. */
 372                cssptr = hmm_alloc(map->length, HMM_BO_PRIVATE, 0, NULL,
 373                                   map->flags & ATOMISP_MAP_FLAG_CACHED);
 374        }
 375
 376        if (!cssptr)
 377                return -ENOMEM;
 378
 379        atomisp_map = kmalloc(sizeof(*atomisp_map), GFP_KERNEL);
 380        if (!atomisp_map) {
 381                hmm_free(cssptr);
 382                return -ENOMEM;
 383        }
 384        atomisp_map->ptr = cssptr;
 385        atomisp_map->length = map->length;
 386        list_add(&atomisp_map->list, &asd->acc.memory_maps);
 387
 388        dev_dbg(asd->isp->dev, "%s: userptr %p, css_address 0x%x, size %d\n",
 389                __func__, map->user_ptr, cssptr, map->length);
 390        map->css_ptr = cssptr;
 391        return 0;
 392}
 393
 394int atomisp_acc_unmap(struct atomisp_sub_device *asd,
 395                      struct atomisp_acc_map *map)
 396{
 397        struct atomisp_map *atomisp_map;
 398
 399        if (asd->acc.pipeline)
 400                return -EBUSY;
 401
 402        atomisp_map = acc_get_map(asd, map->css_ptr, map->length);
 403        if (!atomisp_map)
 404                return -EINVAL;
 405
 406        list_del(&atomisp_map->list);
 407        hmm_free(atomisp_map->ptr);
 408        kfree(atomisp_map);
 409        return 0;
 410}
 411
 412int atomisp_acc_s_mapped_arg(struct atomisp_sub_device *asd,
 413                             struct atomisp_acc_s_mapped_arg *arg)
 414{
 415        struct atomisp_acc_fw *acc_fw;
 416
 417        if (arg->memory >= ATOMISP_ACC_NR_MEMORY)
 418                return -EINVAL;
 419
 420        if (asd->acc.pipeline)
 421                return -EBUSY;
 422
 423        acc_fw = acc_get_fw(asd, arg->fw_handle);
 424        if (!acc_fw)
 425                return -EINVAL;
 426
 427        if (arg->css_ptr != 0 || arg->length != 0) {
 428                /* Unless the parameter is cleared, check that it exists */
 429                if (!acc_get_map(asd, arg->css_ptr, arg->length))
 430                        return -EINVAL;
 431        }
 432
 433        acc_fw->args[arg->memory].length = arg->length;
 434        acc_fw->args[arg->memory].css_ptr = arg->css_ptr;
 435
 436        dev_dbg(asd->isp->dev, "%s: mem %d, address %p, size %ld\n",
 437                __func__, arg->memory, (void *)arg->css_ptr,
 438                (unsigned long)arg->length);
 439        return 0;
 440}
 441
 442/*
 443 * Appends the loaded acceleration binary extensions to the
 444 * current ISP mode. Must be called just before sh_css_start().
 445 */
 446int atomisp_acc_load_extensions(struct atomisp_sub_device *asd)
 447{
 448        struct atomisp_acc_fw *acc_fw;
 449        bool ext_loaded = false;
 450        bool continuous = asd->continuous_mode->val &&
 451                          asd->run_mode->val == ATOMISP_RUN_MODE_PREVIEW;
 452        int ret = 0, i = -1;
 453        struct atomisp_device *isp = asd->isp;
 454
 455        if (asd->acc.pipeline || asd->acc.extension_mode)
 456                return -EBUSY;
 457
 458        /* Invalidate caches. FIXME: should flush only necessary buffers */
 459        wbinvd();
 460
 461        list_for_each_entry(acc_fw, &asd->acc.fw, list) {
 462                if (acc_fw->type != ATOMISP_ACC_FW_LOAD_TYPE_OUTPUT &&
 463                    acc_fw->type != ATOMISP_ACC_FW_LOAD_TYPE_VIEWFINDER)
 464                        continue;
 465
 466                for (i = 0; i < ARRAY_SIZE(acc_flag_to_pipe); i++) {
 467                        /*
 468                         * QoS (ACC pipe) acceleration stages are
 469                         * currently allowed only in continuous mode.
 470                         * Skip them for all other modes.
 471                         */
 472                        if (!continuous &&
 473                            acc_flag_to_pipe[i].flag ==
 474                            ATOMISP_ACC_FW_LOAD_FL_ACC)
 475                                continue;
 476
 477                        if (acc_fw->flags & acc_flag_to_pipe[i].flag) {
 478                                ret = atomisp_css_load_acc_extension(asd,
 479                                                                     acc_fw->fw,
 480                                                                     acc_flag_to_pipe[i].pipe_id,
 481                                                                     acc_fw->type);
 482                                if (ret)
 483                                        goto error;
 484
 485                                ext_loaded = true;
 486                        }
 487                }
 488
 489                ret = atomisp_css_set_acc_parameters(acc_fw);
 490                if (ret < 0)
 491                        goto error;
 492        }
 493
 494        if (!ext_loaded)
 495                return ret;
 496
 497        ret = atomisp_css_update_stream(asd);
 498        if (ret) {
 499                dev_err(isp->dev, "%s: update stream failed.\n", __func__);
 500                goto error;
 501        }
 502
 503        asd->acc.extension_mode = true;
 504        return 0;
 505
 506error:
 507        while (--i >= 0) {
 508                if (acc_fw->flags & acc_flag_to_pipe[i].flag) {
 509                        atomisp_css_unload_acc_extension(asd, acc_fw->fw,
 510                                                         acc_flag_to_pipe[i].pipe_id);
 511                }
 512        }
 513
 514        list_for_each_entry_continue_reverse(acc_fw, &asd->acc.fw, list) {
 515                if (acc_fw->type != ATOMISP_ACC_FW_LOAD_TYPE_OUTPUT &&
 516                    acc_fw->type != ATOMISP_ACC_FW_LOAD_TYPE_VIEWFINDER)
 517                        continue;
 518
 519                for (i = ARRAY_SIZE(acc_flag_to_pipe) - 1; i >= 0; i--) {
 520                        if (!continuous &&
 521                            acc_flag_to_pipe[i].flag ==
 522                            ATOMISP_ACC_FW_LOAD_FL_ACC)
 523                                continue;
 524                        if (acc_fw->flags & acc_flag_to_pipe[i].flag) {
 525                                atomisp_css_unload_acc_extension(asd,
 526                                                                 acc_fw->fw,
 527                                                                 acc_flag_to_pipe[i].pipe_id);
 528                        }
 529                }
 530        }
 531        return ret;
 532}
 533
 534void atomisp_acc_unload_extensions(struct atomisp_sub_device *asd)
 535{
 536        struct atomisp_acc_fw *acc_fw;
 537        int i;
 538
 539        if (!asd->acc.extension_mode)
 540                return;
 541
 542        list_for_each_entry_reverse(acc_fw, &asd->acc.fw, list) {
 543                if (acc_fw->type != ATOMISP_ACC_FW_LOAD_TYPE_OUTPUT &&
 544                    acc_fw->type != ATOMISP_ACC_FW_LOAD_TYPE_VIEWFINDER)
 545                        continue;
 546
 547                for (i = ARRAY_SIZE(acc_flag_to_pipe) - 1; i >= 0; i--) {
 548                        if (acc_fw->flags & acc_flag_to_pipe[i].flag) {
 549                                atomisp_css_unload_acc_extension(asd,
 550                                                                 acc_fw->fw,
 551                                                                 acc_flag_to_pipe[i].pipe_id);
 552                        }
 553                }
 554        }
 555
 556        asd->acc.extension_mode = false;
 557}
 558
 559int atomisp_acc_set_state(struct atomisp_sub_device *asd,
 560                          struct atomisp_acc_state *arg)
 561{
 562        struct atomisp_acc_fw *acc_fw;
 563        bool enable = (arg->flags & ATOMISP_STATE_FLAG_ENABLE) != 0;
 564        struct ia_css_pipe *pipe;
 565        int r;
 566        int i;
 567
 568        if (!asd->acc.extension_mode)
 569                return -EBUSY;
 570
 571        if (arg->flags & ~ATOMISP_STATE_FLAG_ENABLE)
 572                return -EINVAL;
 573
 574        acc_fw = acc_get_fw(asd, arg->fw_handle);
 575        if (!acc_fw)
 576                return -EINVAL;
 577
 578        if (enable)
 579                wbinvd();
 580
 581        for (i = 0; i < ARRAY_SIZE(acc_flag_to_pipe); i++) {
 582                if (acc_fw->flags & acc_flag_to_pipe[i].flag) {
 583                        pipe = asd->stream_env[ATOMISP_INPUT_STREAM_GENERAL].
 584                               pipes[acc_flag_to_pipe[i].pipe_id];
 585                        r = ia_css_pipe_set_qos_ext_state(pipe, acc_fw->handle,
 586                                                          enable);
 587                        if (r)
 588                                return -EBADRQC;
 589                }
 590        }
 591
 592        if (enable)
 593                acc_fw->flags |= ATOMISP_ACC_FW_LOAD_FL_ENABLE;
 594        else
 595                acc_fw->flags &= ~ATOMISP_ACC_FW_LOAD_FL_ENABLE;
 596
 597        return 0;
 598}
 599
 600int atomisp_acc_get_state(struct atomisp_sub_device *asd,
 601                          struct atomisp_acc_state *arg)
 602{
 603        struct atomisp_acc_fw *acc_fw;
 604
 605        if (!asd->acc.extension_mode)
 606                return -EBUSY;
 607
 608        acc_fw = acc_get_fw(asd, arg->fw_handle);
 609        if (!acc_fw)
 610                return -EINVAL;
 611
 612        arg->flags = acc_fw->flags;
 613
 614        return 0;
 615}
 616